Compare commits
5 Commits
b5364a2bbc
...
cf13fb78b5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf13fb78b5 | ||
|
|
034654c308 | ||
|
|
64c304bb6d | ||
|
|
689e177c99 | ||
|
|
e18f1b9963 |
@@ -87,18 +87,6 @@ MonoBehaviour:
|
|||||||
m_SerializedLabels:
|
m_SerializedLabels:
|
||||||
- BlokkemonCard
|
- BlokkemonCard
|
||||||
FlaggedDuringContentUpdateRestriction: 0
|
FlaggedDuringContentUpdateRestriction: 0
|
||||||
- m_GUID: 80e3766cc597fd94f895f5cd6aa2bcc6
|
|
||||||
m_Address: Assets/Data/Cards/Card_New Card.asset
|
|
||||||
m_ReadOnly: 0
|
|
||||||
m_SerializedLabels:
|
|
||||||
- BlokkemonCard
|
|
||||||
FlaggedDuringContentUpdateRestriction: 0
|
|
||||||
- m_GUID: 82008856df7c51f47b1582de464ba44b
|
|
||||||
m_Address: Assets/Data/Cards/Card_New Card.asset
|
|
||||||
m_ReadOnly: 0
|
|
||||||
m_SerializedLabels:
|
|
||||||
- BlokkemonCard
|
|
||||||
FlaggedDuringContentUpdateRestriction: 0
|
|
||||||
- m_GUID: 99d8e528a8f9ead438e4c88a08c6f6c0
|
- m_GUID: 99d8e528a8f9ead438e4c88a08c6f6c0
|
||||||
m_Address: Assets/Data/Cards/Card_New Card.asset
|
m_Address: Assets/Data/Cards/Card_New Card.asset
|
||||||
m_ReadOnly: 0
|
m_ReadOnly: 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4f4ec75013bc276429c2f4fa52d165e0
|
guid: bcbebd216e6c867409206d33b4395b5b
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 11400000
|
mainObjectFileID: 11400000
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 53996921ed2094948aa317efe4ca6630
|
guid: 7e3c8a4745009804b9a620e3ae15070f
|
||||||
NativeFormatImporter:
|
NativeFormatImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 11400000
|
mainObjectFileID: 11400000
|
||||||
|
|||||||
@@ -216,6 +216,13 @@ namespace Editor.CardSystem
|
|||||||
|
|
||||||
EditorGUILayout.Space(5);
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
if (GUILayout.Button("Populate Pending (6 Unique Cards)", GUILayout.Height(30)))
|
||||||
|
{
|
||||||
|
PopulatePendingCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
// Danger Zone
|
// Danger Zone
|
||||||
EditorGUILayout.Space(10);
|
EditorGUILayout.Space(10);
|
||||||
EditorGUILayout.LabelField("Danger Zone", EditorStyles.miniBoldLabel);
|
EditorGUILayout.LabelField("Danger Zone", EditorStyles.miniBoldLabel);
|
||||||
@@ -304,6 +311,53 @@ namespace Editor.CardSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void PopulatePendingCards()
|
||||||
|
{
|
||||||
|
if (CardSystemManager.Instance != null)
|
||||||
|
{
|
||||||
|
List<CardDefinition> allDefinitions = CardSystemManager.Instance.GetAllCardDefinitions();
|
||||||
|
|
||||||
|
if (allDefinitions.Count == 0)
|
||||||
|
{
|
||||||
|
lastActionMessage = "Error: No card definitions available";
|
||||||
|
Logging.Warning($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||||
|
Repaint();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have enough definitions to pick 6 unique cards
|
||||||
|
int cardsToAdd = Mathf.Min(6, allDefinitions.Count);
|
||||||
|
|
||||||
|
// Shuffle definitions to get random unique cards
|
||||||
|
List<CardDefinition> shuffledDefs = new List<CardDefinition>(allDefinitions);
|
||||||
|
for (int i = 0; i < shuffledDefs.Count; i++)
|
||||||
|
{
|
||||||
|
int randomIndex = Random.Range(i, shuffledDefs.Count);
|
||||||
|
(shuffledDefs[i], shuffledDefs[randomIndex]) = (shuffledDefs[randomIndex], shuffledDefs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add first 6 unique cards to pending
|
||||||
|
// AddCardToInventoryDelayed automatically routes NEW cards to pending queue
|
||||||
|
int cardsAdded = 0;
|
||||||
|
for (int i = 0; i < cardsToAdd; i++)
|
||||||
|
{
|
||||||
|
CardData newCard = shuffledDefs[i].CreateCardData();
|
||||||
|
CardSystemManager.Instance.AddCardToInventoryDelayed(newCard);
|
||||||
|
cardsAdded++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastActionMessage = $"Added {cardsAdded} unique cards to pending reveal queue";
|
||||||
|
Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||||
|
RefreshDebugInfo();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||||
|
Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ClearAllCards()
|
private void ClearAllCards()
|
||||||
{
|
{
|
||||||
if (CardSystemManager.Instance != null)
|
if (CardSystemManager.Instance != null)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -824,6 +824,10 @@ PrefabInstance:
|
|||||||
propertyPath: m_SizeDelta.x
|
propertyPath: m_SizeDelta.x
|
||||||
value: 0
|
value: 0
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 2685537002028647152, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||||
|
propertyPath: interactable
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 2717636461124059971, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
- target: {fileID: 2717636461124059971, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||||
propertyPath: m_SizeDelta.x
|
propertyPath: m_SizeDelta.x
|
||||||
value: 0
|
value: 0
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
Assets/Scripts/CardSystem/Controllers.meta
Normal file
3
Assets/Scripts/CardSystem/Controllers.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: abf7a5def55e43178a4c88caf5686cc9
|
||||||
|
timeCreated: 1763454388
|
||||||
172
Assets/Scripts/CardSystem/Controllers/AlbumNavigationService.cs
Normal file
172
Assets/Scripts/CardSystem/Controllers/AlbumNavigationService.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using AppleHills.Data.CardSystem;
|
||||||
|
using Core;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UI.CardSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages album page navigation, zone mapping, and page flip tracking.
|
||||||
|
/// Created and owned by AlbumViewPage (not a Unity component).
|
||||||
|
/// </summary>
|
||||||
|
public class AlbumNavigationService
|
||||||
|
{
|
||||||
|
private readonly BookCurlPro.BookPro _book;
|
||||||
|
private readonly BookTabButton[] _zoneTabs;
|
||||||
|
|
||||||
|
private bool _isPageFlipping = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the book currently flipping to a page?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPageFlipping => _isPageFlipping;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor - called by AlbumViewPage's lazy property
|
||||||
|
/// </summary>
|
||||||
|
public AlbumNavigationService(BookCurlPro.BookPro book, BookTabButton[] zoneTabs)
|
||||||
|
{
|
||||||
|
_book = book;
|
||||||
|
_zoneTabs = zoneTabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if we're currently viewing the album proper (not the menu page)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsInAlbumProper()
|
||||||
|
{
|
||||||
|
if (_book == null)
|
||||||
|
{
|
||||||
|
Logging.Warning("[AlbumNavigationService] Book reference is null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page 1 is the menu/cover, page 2+ are album pages with card slots
|
||||||
|
return _book.CurrentPaper > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Navigate to the page where a specific card belongs
|
||||||
|
/// </summary>
|
||||||
|
public void NavigateToCardPage(CardData cardData, System.Action onComplete)
|
||||||
|
{
|
||||||
|
if (cardData == null || _book == null)
|
||||||
|
{
|
||||||
|
onComplete?.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find target page based on card's zone
|
||||||
|
int targetPage = FindPageForZone(cardData.Zone);
|
||||||
|
|
||||||
|
if (targetPage < 0)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[AlbumNavigationService] No page found for zone {cardData.Zone}");
|
||||||
|
onComplete?.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as flipping
|
||||||
|
_isPageFlipping = true;
|
||||||
|
Logging.Debug($"[AlbumNavigationService] Starting page flip to page {targetPage}");
|
||||||
|
|
||||||
|
// Get or add AutoFlip component
|
||||||
|
BookCurlPro.AutoFlip autoFlip = _book.GetComponent<BookCurlPro.AutoFlip>();
|
||||||
|
if (autoFlip == null)
|
||||||
|
{
|
||||||
|
autoFlip = _book.gameObject.AddComponent<BookCurlPro.AutoFlip>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start flipping with callback
|
||||||
|
autoFlip.enabled = true;
|
||||||
|
autoFlip.StartFlipping(targetPage, () =>
|
||||||
|
{
|
||||||
|
// Mark as complete
|
||||||
|
_isPageFlipping = false;
|
||||||
|
Logging.Debug($"[AlbumNavigationService] Page flip to {targetPage} completed");
|
||||||
|
|
||||||
|
// Call original callback if provided
|
||||||
|
onComplete?.Invoke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get list of card definition IDs on the current page
|
||||||
|
/// </summary>
|
||||||
|
public List<string> GetDefinitionsOnCurrentPage()
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
if (_book == null) return result;
|
||||||
|
|
||||||
|
int currentPage = _book.CurrentPaper;
|
||||||
|
|
||||||
|
// Find all AlbumCardSlot in scene
|
||||||
|
var allSlots = Object.FindObjectsByType<AlbumCardSlot>(FindObjectsSortMode.None);
|
||||||
|
|
||||||
|
foreach (var slot in allSlots)
|
||||||
|
{
|
||||||
|
if (IsSlotOnPage(slot.transform, currentPage))
|
||||||
|
{
|
||||||
|
if (slot.TargetCardDefinition != null && !string.IsNullOrEmpty(slot.TargetCardDefinition.Id))
|
||||||
|
{
|
||||||
|
result.Add(slot.TargetCardDefinition.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find the target page for a card zone using BookTabButtons
|
||||||
|
/// </summary>
|
||||||
|
private int FindPageForZone(CardZone zone)
|
||||||
|
{
|
||||||
|
if (_zoneTabs == null || _zoneTabs.Length == 0)
|
||||||
|
{
|
||||||
|
Logging.Warning("[AlbumNavigationService] No zone tabs available!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var tab in _zoneTabs)
|
||||||
|
{
|
||||||
|
if (tab.Zone == zone)
|
||||||
|
{
|
||||||
|
return tab.TargetPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.Warning($"[AlbumNavigationService] No BookTabButton found for zone {zone}");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a slot's transform is on a specific book page
|
||||||
|
/// </summary>
|
||||||
|
private bool IsSlotOnPage(Transform slotTransform, int pageIndex)
|
||||||
|
{
|
||||||
|
if (_book == null || _book.papers == null || pageIndex < 0 || pageIndex >= _book.papers.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var paper = _book.papers[pageIndex];
|
||||||
|
if (paper == null) return false;
|
||||||
|
|
||||||
|
// Check if slotTransform parent hierarchy contains paper.Front or paper.Back
|
||||||
|
Transform current = slotTransform;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
if ((paper.Front != null && current.gameObject == paper.Front) ||
|
||||||
|
(paper.Back != null && current.gameObject == paper.Back))
|
||||||
|
return true;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5dce94e276b8456996d9ddacef25c744
|
||||||
|
timeCreated: 1763425777
|
||||||
128
Assets/Scripts/CardSystem/Controllers/CardEnlargeController.cs
Normal file
128
Assets/Scripts/CardSystem/Controllers/CardEnlargeController.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using Core;
|
||||||
|
using UI.CardSystem.StateMachine;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UI.CardSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages card enlarge system: backdrop display and card reparenting.
|
||||||
|
/// Created and owned by AlbumViewPage (not a Unity component).
|
||||||
|
/// </summary>
|
||||||
|
public class CardEnlargeController
|
||||||
|
{
|
||||||
|
private readonly GameObject _backdrop;
|
||||||
|
private readonly Transform _enlargedContainer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor - called by AlbumViewPage's lazy property
|
||||||
|
/// </summary>
|
||||||
|
public CardEnlargeController(GameObject backdrop, Transform enlargedContainer)
|
||||||
|
{
|
||||||
|
_backdrop = backdrop;
|
||||||
|
_enlargedContainer = enlargedContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribe to a placed card's enlarged state events
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterCard(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
if (card == null) return;
|
||||||
|
|
||||||
|
var enlargeState = card.GetStateComponent<StateMachine.States.CardAlbumEnlargedState>(CardStateNames.AlbumEnlarged);
|
||||||
|
if (enlargeState != null)
|
||||||
|
{
|
||||||
|
enlargeState.OnEnlargeRequested += OnCardEnlargeRequested;
|
||||||
|
enlargeState.OnShrinkRequested += OnCardShrinkRequested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unsubscribe from a card's enlarged state events
|
||||||
|
/// </summary>
|
||||||
|
public void UnregisterCard(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
if (card == null) return;
|
||||||
|
|
||||||
|
var enlargeState = card.GetStateComponent<StateMachine.States.CardAlbumEnlargedState>(CardStateNames.AlbumEnlarged);
|
||||||
|
if (enlargeState != null)
|
||||||
|
{
|
||||||
|
enlargeState.OnEnlargeRequested -= OnCardEnlargeRequested;
|
||||||
|
enlargeState.OnShrinkRequested -= OnCardShrinkRequested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up enlarged card state (on page close)
|
||||||
|
/// </summary>
|
||||||
|
public void CleanupEnlargedState()
|
||||||
|
{
|
||||||
|
// Hide backdrop if visible
|
||||||
|
if (_backdrop != null && _backdrop.activeSelf)
|
||||||
|
{
|
||||||
|
_backdrop.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's an enlarged card in the container, return it to its slot
|
||||||
|
if (_enlargedContainer != null && _enlargedContainer.childCount > 0)
|
||||||
|
{
|
||||||
|
for (int i = _enlargedContainer.childCount - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Transform cardTransform = _enlargedContainer.GetChild(i);
|
||||||
|
var card = cardTransform.GetComponent<StateMachine.Card>();
|
||||||
|
var state = cardTransform.GetComponentInChildren<StateMachine.States.CardAlbumEnlargedState>(true);
|
||||||
|
if (card != null && state != null)
|
||||||
|
{
|
||||||
|
Transform originalParent = state.GetOriginalParent();
|
||||||
|
if (originalParent != null)
|
||||||
|
{
|
||||||
|
cardTransform.SetParent(originalParent, true);
|
||||||
|
cardTransform.localPosition = state.GetOriginalLocalPosition();
|
||||||
|
cardTransform.localRotation = state.GetOriginalLocalRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
|
||||||
|
private void OnCardEnlargeRequested(StateMachine.States.CardAlbumEnlargedState state)
|
||||||
|
{
|
||||||
|
if (state == null) return;
|
||||||
|
|
||||||
|
// Show backdrop
|
||||||
|
if (_backdrop != null)
|
||||||
|
{
|
||||||
|
_backdrop.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reparent card root to enlarged container preserving world transform
|
||||||
|
if (_enlargedContainer != null)
|
||||||
|
{
|
||||||
|
var ctx = state.GetComponentInParent<StateMachine.CardContext>();
|
||||||
|
if (ctx != null)
|
||||||
|
{
|
||||||
|
ctx.RootTransform.SetParent(_enlargedContainer, true);
|
||||||
|
ctx.RootTransform.SetAsLastSibling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCardShrinkRequested(StateMachine.States.CardAlbumEnlargedState state)
|
||||||
|
{
|
||||||
|
if (state == null) return;
|
||||||
|
|
||||||
|
// Hide backdrop; state will animate back to slot and reparent on completion
|
||||||
|
if (_backdrop != null)
|
||||||
|
{
|
||||||
|
_backdrop.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not reparent here; reverse animation is orchestrated by the state
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f0c2c6c12ee64ccabe778e848ac9c822
|
||||||
|
timeCreated: 1763425800
|
||||||
392
Assets/Scripts/CardSystem/Controllers/CornerCardManager.cs
Normal file
392
Assets/Scripts/CardSystem/Controllers/CornerCardManager.cs
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AppleHills.Data.CardSystem;
|
||||||
|
using Core;
|
||||||
|
using Data.CardSystem;
|
||||||
|
using UI.DragAndDrop.Core;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UI.CardSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages corner card spawning, selection, shuffling, and rebuilding.
|
||||||
|
/// Created and owned by AlbumViewPage (not a Unity component).
|
||||||
|
/// </summary>
|
||||||
|
public class CornerCardManager
|
||||||
|
{
|
||||||
|
private readonly SlotContainer _slots;
|
||||||
|
private readonly GameObject _cardPrefab;
|
||||||
|
private readonly AlbumViewPage _owner;
|
||||||
|
|
||||||
|
private List<StateMachine.Card> _pendingCards = new List<StateMachine.Card>();
|
||||||
|
private const int MaxVisible = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor - called by AlbumViewPage's lazy property
|
||||||
|
/// </summary>
|
||||||
|
public CornerCardManager(SlotContainer slots, GameObject cardPrefab, AlbumViewPage owner)
|
||||||
|
{
|
||||||
|
_slots = slots;
|
||||||
|
_cardPrefab = cardPrefab;
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the list of currently spawned corner cards
|
||||||
|
/// </summary>
|
||||||
|
public List<StateMachine.Card> PendingCards => _pendingCards;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spawn pending corner cards with initial setup
|
||||||
|
/// </summary>
|
||||||
|
public void SpawnCards()
|
||||||
|
{
|
||||||
|
RebuildCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rebuild corner card display incrementally.
|
||||||
|
/// Flow: 1) Shuffle remaining cards to front slots (0→1→2)
|
||||||
|
/// 2) Spawn new card in last slot if needed
|
||||||
|
/// Called on initial spawn and after card is removed.
|
||||||
|
/// </summary>
|
||||||
|
public void RebuildCards()
|
||||||
|
{
|
||||||
|
if (_cardPrefab == null || _slots == null) return;
|
||||||
|
|
||||||
|
// Step 1: Determine how many cards should be displayed
|
||||||
|
var uniquePending = GetUniquePendingCards();
|
||||||
|
int totalPendingCards = uniquePending.Count;
|
||||||
|
int cardsToDisplay = Mathf.Min(totalPendingCards, MaxVisible);
|
||||||
|
|
||||||
|
int currentCardCount = _pendingCards.Count;
|
||||||
|
|
||||||
|
Logging.Debug($"[CornerCardManager] RebuildCards: current={currentCardCount}, target={cardsToDisplay}");
|
||||||
|
|
||||||
|
// Step 2: Remove excess cards if we have too many
|
||||||
|
while (_pendingCards.Count > cardsToDisplay)
|
||||||
|
{
|
||||||
|
int lastIndex = _pendingCards.Count - 1;
|
||||||
|
var cardToRemove = _pendingCards[lastIndex];
|
||||||
|
|
||||||
|
if (cardToRemove != null)
|
||||||
|
{
|
||||||
|
if (cardToRemove.Context != null)
|
||||||
|
{
|
||||||
|
cardToRemove.Context.OnDragStarted -= OnCardDragStarted;
|
||||||
|
}
|
||||||
|
Object.Destroy(cardToRemove.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingCards.RemoveAt(lastIndex);
|
||||||
|
Logging.Debug($"[CornerCardManager] Removed excess card, now have {_pendingCards.Count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Shuffle remaining cards to occupy first slots (0, 1, 2)
|
||||||
|
ShuffleCardsToFrontSlots();
|
||||||
|
|
||||||
|
// Step 4: Spawn new cards in remaining slots if needed
|
||||||
|
while (_pendingCards.Count < cardsToDisplay)
|
||||||
|
{
|
||||||
|
int slotIndex = _pendingCards.Count; // Next available slot
|
||||||
|
var slot = FindSlotByIndex(slotIndex);
|
||||||
|
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[CornerCardManager] Slot {slotIndex} not found, stopping spawn");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnCardInSlot(slot);
|
||||||
|
Logging.Debug($"[CornerCardManager] Added new card in slot {slotIndex}, now have {_pendingCards.Count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardsToDisplay == 0)
|
||||||
|
{
|
||||||
|
Logging.Debug("[CornerCardManager] No pending cards to display in corner");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Smart card selection from pending queue.
|
||||||
|
/// Prioritizes cards that belong on current album page, falls back to random.
|
||||||
|
/// Removes selected card from pending and rebuilds corner.
|
||||||
|
/// </summary>
|
||||||
|
public CardData GetSmartSelection()
|
||||||
|
{
|
||||||
|
if (CardSystemManager.Instance == null) return null;
|
||||||
|
var pending = CardSystemManager.Instance.GetPendingRevealCards();
|
||||||
|
if (pending.Count == 0) return null;
|
||||||
|
|
||||||
|
// Try current page match
|
||||||
|
var pageDefs = _owner.GetDefinitionsOnCurrentPage();
|
||||||
|
var match = pending.Find(c => pageDefs.Contains(c.DefinitionId));
|
||||||
|
|
||||||
|
// If no match, use random
|
||||||
|
if (match == null)
|
||||||
|
{
|
||||||
|
int idx = Random.Range(0, pending.Count);
|
||||||
|
match = pending[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Remove from pending list immediately
|
||||||
|
// Card is now in "reveal flow" and will be added to collection when placed
|
||||||
|
if (match != null)
|
||||||
|
{
|
||||||
|
// Remove from pending using the manager (fires OnPendingCardRemoved event)
|
||||||
|
CardSystemManager.Instance.RemoveFromPending(match);
|
||||||
|
Logging.Debug($"[CornerCardManager] Removed '{match.Name}' from pending cards, starting reveal flow");
|
||||||
|
|
||||||
|
// Rebuild corner cards AFTER removing from pending list
|
||||||
|
// This ensures the removed card doesn't get re-spawned
|
||||||
|
RebuildCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleanup all corner cards (on page close)
|
||||||
|
/// </summary>
|
||||||
|
public void CleanupAllCards()
|
||||||
|
{
|
||||||
|
// First, unsubscribe and destroy tracked cards
|
||||||
|
foreach (var c in _pendingCards)
|
||||||
|
{
|
||||||
|
if (c != null)
|
||||||
|
{
|
||||||
|
if (c.Context != null)
|
||||||
|
{
|
||||||
|
c.Context.OnDragStarted -= OnCardDragStarted;
|
||||||
|
}
|
||||||
|
Object.Destroy(c.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pendingCards.Clear();
|
||||||
|
|
||||||
|
// IMPORTANT: Also clear ALL children from corner slots
|
||||||
|
// This catches cards that were removed from tracking but not destroyed
|
||||||
|
// (e.g., cards being dragged to album)
|
||||||
|
if (_slots != null)
|
||||||
|
{
|
||||||
|
foreach (var slot in _slots.Slots)
|
||||||
|
{
|
||||||
|
if (slot == null || slot.transform == null) continue;
|
||||||
|
|
||||||
|
// Destroy all card children in this slot
|
||||||
|
for (int i = slot.transform.childCount - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var child = slot.transform.GetChild(i);
|
||||||
|
var card = child.GetComponent<StateMachine.Card>();
|
||||||
|
if (card != null)
|
||||||
|
{
|
||||||
|
// Unsubscribe if somehow still subscribed
|
||||||
|
if (card.Context != null)
|
||||||
|
{
|
||||||
|
card.Context.OnDragStarted -= OnCardDragStarted;
|
||||||
|
}
|
||||||
|
Object.Destroy(child.gameObject);
|
||||||
|
Logging.Debug($"[CornerCardManager] Cleaned up orphaned card from slot {slot.SlotIndex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notify that a card has been placed in album (remove from tracking)
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyCardPlaced(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
if (card != null)
|
||||||
|
{
|
||||||
|
// Remove from tracking list
|
||||||
|
_pendingCards.Remove(card);
|
||||||
|
|
||||||
|
// IMPORTANT: Unsubscribe from drag events
|
||||||
|
// Placed cards should never respond to corner drag events
|
||||||
|
if (card.Context != null)
|
||||||
|
{
|
||||||
|
card.Context.OnDragStarted -= OnCardDragStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.Debug($"[CornerCardManager] Card placed and unsubscribed from corner events: {card.CardData?.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shuffle remaining cards to occupy the first available slots (0 → 1 → 2).
|
||||||
|
/// Example: If we have 2 cards in slots 0 and 2, move the card from slot 2 to slot 1.
|
||||||
|
/// </summary>
|
||||||
|
private void ShuffleCardsToFrontSlots()
|
||||||
|
{
|
||||||
|
if (_pendingCards.Count == 0) return;
|
||||||
|
|
||||||
|
Logging.Debug($"[CornerCardManager] Shuffling {_pendingCards.Count} cards to front slots");
|
||||||
|
|
||||||
|
// Reassign each card to the first N slots (0, 1, 2...)
|
||||||
|
for (int i = 0; i < _pendingCards.Count; i++)
|
||||||
|
{
|
||||||
|
var card = _pendingCards[i];
|
||||||
|
var targetSlot = FindSlotByIndex(i);
|
||||||
|
|
||||||
|
if (targetSlot == null)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[CornerCardManager] Could not find slot with index {i} during shuffle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if card is already in the correct slot
|
||||||
|
if (card.CurrentSlot == targetSlot)
|
||||||
|
{
|
||||||
|
// Already in correct slot, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vacate current slot if occupied
|
||||||
|
if (card.CurrentSlot != null)
|
||||||
|
{
|
||||||
|
card.CurrentSlot.Vacate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign to target slot with animation
|
||||||
|
card.AssignToSlot(targetSlot, true); // true = animate
|
||||||
|
Logging.Debug($"[CornerCardManager] Shuffled card {i} to slot {targetSlot.SlotIndex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spawn a single card in the specified slot.
|
||||||
|
/// Card will be in PendingFaceDownState and match slot transform.
|
||||||
|
/// </summary>
|
||||||
|
private void SpawnCardInSlot(DraggableSlot slot)
|
||||||
|
{
|
||||||
|
if (slot == null || _cardPrefab == null) return;
|
||||||
|
|
||||||
|
// Instantiate card as child of slot (not container)
|
||||||
|
GameObject cardObj = Object.Instantiate(_cardPrefab, slot.transform);
|
||||||
|
var card = cardObj.GetComponent<StateMachine.Card>();
|
||||||
|
|
||||||
|
if (card == null)
|
||||||
|
{
|
||||||
|
Logging.Warning("[CornerCardManager] Card prefab missing Card component!");
|
||||||
|
Object.Destroy(cardObj);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Assign to slot FIRST (establishes correct transform)
|
||||||
|
card.AssignToSlot(slot, false); // false = instant, no animation
|
||||||
|
|
||||||
|
// Inject AlbumViewPage dependency
|
||||||
|
card.Context.SetAlbumViewPage(_owner);
|
||||||
|
|
||||||
|
// THEN setup card for pending state (transitions to PendingFaceDownState)
|
||||||
|
// This ensures OriginalScale is captured AFTER slot assignment
|
||||||
|
card.SetupForAlbumPending();
|
||||||
|
|
||||||
|
// Subscribe to drag events for reorganization
|
||||||
|
card.Context.OnDragStarted += OnCardDragStarted;
|
||||||
|
|
||||||
|
// Track in list
|
||||||
|
_pendingCards.Add(card);
|
||||||
|
|
||||||
|
Logging.Debug($"[CornerCardManager] Spawned card in slot {slot.SlotIndex}, state: {card.GetCurrentStateName()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle card drag started - cleanup and unparent from corner slot
|
||||||
|
/// Rebuild happens in GetSmartSelection after pending list is updated
|
||||||
|
/// </summary>
|
||||||
|
private void OnCardDragStarted(StateMachine.CardContext context)
|
||||||
|
{
|
||||||
|
if (context == null) return;
|
||||||
|
|
||||||
|
var card = context.GetComponent<StateMachine.Card>();
|
||||||
|
if (card == null) return;
|
||||||
|
|
||||||
|
// Only handle pending corner cards
|
||||||
|
if (!_pendingCards.Contains(card)) return;
|
||||||
|
|
||||||
|
Logging.Debug($"[CornerCardManager] Card drag started, removing from corner");
|
||||||
|
|
||||||
|
// 1. Remove from tracking (card is transitioning to placement flow)
|
||||||
|
_pendingCards.Remove(card);
|
||||||
|
|
||||||
|
// 2. Unsubscribe from this card's events
|
||||||
|
if (card.Context != null)
|
||||||
|
{
|
||||||
|
card.Context.OnDragStarted -= OnCardDragStarted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. CRITICAL: Unparent from corner slot BEFORE rebuild happens
|
||||||
|
// This prevents the card from being destroyed when CleanupAllCards runs
|
||||||
|
// Reparent to page's transform to keep it alive during drag
|
||||||
|
if (card.transform.parent != null)
|
||||||
|
{
|
||||||
|
card.transform.SetParent(_owner.transform, true); // Keep world position
|
||||||
|
Logging.Debug($"[CornerCardManager] Card unparented from corner slot - safe for rebuild");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: RebuildCards() is called in GetSmartSelection()
|
||||||
|
// after the card is removed from CardSystemManager's pending list
|
||||||
|
// The card is now safe from being destroyed since it's no longer a child of corner slots
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get unique pending cards (one per definition+rarity combo)
|
||||||
|
/// </summary>
|
||||||
|
private List<CardData> GetUniquePendingCards()
|
||||||
|
{
|
||||||
|
if (CardSystemManager.Instance == null) return new List<CardData>();
|
||||||
|
|
||||||
|
var pending = CardSystemManager.Instance.GetPendingRevealCards();
|
||||||
|
|
||||||
|
// Group by definition+rarity, take first of each group
|
||||||
|
var uniqueDict = new Dictionary<string, CardData>();
|
||||||
|
int duplicateCount = 0;
|
||||||
|
|
||||||
|
foreach (var card in pending)
|
||||||
|
{
|
||||||
|
string key = $"{card.DefinitionId}_{card.Rarity}";
|
||||||
|
if (!uniqueDict.ContainsKey(key))
|
||||||
|
{
|
||||||
|
uniqueDict[key] = card;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duplicateCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateCount > 0)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[CornerCardManager] Found {duplicateCount} duplicate pending cards (same definition+rarity combo)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<CardData>(uniqueDict.Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find a slot by its SlotIndex property
|
||||||
|
/// </summary>
|
||||||
|
private DraggableSlot FindSlotByIndex(int slotIndex)
|
||||||
|
{
|
||||||
|
if (_slots == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach (var slot in _slots.Slots)
|
||||||
|
{
|
||||||
|
if (slot.SlotIndex == slotIndex)
|
||||||
|
{
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7c68770a6974aa4a22c645fe0517fe2
|
||||||
|
timeCreated: 1763425113
|
||||||
3
Assets/Scripts/CardSystem/Core.meta
Normal file
3
Assets/Scripts/CardSystem/Core.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 619e69d1b6e44ecabc40657b2bcd13f9
|
||||||
|
timeCreated: 1763454353
|
||||||
@@ -24,9 +24,9 @@ namespace UI.CardSystem.StateMachine
|
|||||||
public CardData CardData => context?.CardData;
|
public CardData CardData => context?.CardData;
|
||||||
|
|
||||||
// State inspection properties for booster flow
|
// State inspection properties for booster flow
|
||||||
public bool IsIdle => GetCurrentStateName() == "IdleState";
|
public bool IsIdle => GetCurrentStateName() == CardStateNames.Idle;
|
||||||
public bool IsRevealing => !IsIdle && !IsComplete;
|
public bool IsRevealing => !IsIdle && !IsComplete;
|
||||||
public bool IsComplete => context?.HasCompletedReveal ?? false;
|
public bool IsComplete => context?.BoosterContext.HasCompletedReveal ?? false;
|
||||||
|
|
||||||
// Event fired when this card is successfully placed into an AlbumCardSlot
|
// Event fired when this card is successfully placed into an AlbumCardSlot
|
||||||
public event System.Action<Card, AlbumCardSlot> OnPlacedInAlbumSlot;
|
public event System.Action<Card, AlbumCardSlot> OnPlacedInAlbumSlot;
|
||||||
@@ -89,23 +89,23 @@ namespace UI.CardSystem.StateMachine
|
|||||||
|
|
||||||
// Default behavior for states that don't implement custom drag end
|
// Default behavior for states that don't implement custom drag end
|
||||||
string current = GetCurrentStateName();
|
string current = GetCurrentStateName();
|
||||||
if (current == "DraggingState")
|
if (current == CardStateNames.Dragging)
|
||||||
{
|
{
|
||||||
if (CurrentSlot is AlbumCardSlot albumSlot)
|
if (CurrentSlot is AlbumCardSlot albumSlot)
|
||||||
{
|
{
|
||||||
Logging.Debug($"[Card] Dropped in album slot, transitioning to PlacedInSlotState");
|
Logging.Debug($"[Card] Dropped in album slot, transitioning to PlacedInSlotState");
|
||||||
var placedState = GetStateComponent<States.CardPlacedInSlotState>("PlacedInSlotState");
|
var placedState = GetStateComponent<States.CardPlacedInSlotState>(CardStateNames.PlacedInSlot);
|
||||||
if (placedState != null)
|
if (placedState != null)
|
||||||
{
|
{
|
||||||
placedState.SetParentSlot(albumSlot);
|
placedState.SetParentSlot(albumSlot);
|
||||||
}
|
}
|
||||||
ChangeState("PlacedInSlotState");
|
ChangeState(CardStateNames.PlacedInSlot);
|
||||||
OnPlacedInAlbumSlot?.Invoke(this, albumSlot);
|
OnPlacedInAlbumSlot?.Invoke(this, albumSlot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logging.Debug("[Card] Dropped outside valid slot, returning to RevealedState");
|
Logging.Debug("[Card] Dropped outside valid slot, returning to RevealedState");
|
||||||
ChangeState("RevealedState");
|
ChangeState(CardStateNames.Revealed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ namespace UI.CardSystem.StateMachine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetupForBoosterReveal(CardData data, bool isNew)
|
public void SetupForBoosterReveal(CardData data, bool isNew)
|
||||||
{
|
{
|
||||||
SetupCard(data, "IdleState");
|
SetupCard(data, CardStateNames.Idle);
|
||||||
SetDraggingEnabled(false); // Booster cards cannot be dragged
|
SetDraggingEnabled(false); // Booster cards cannot be dragged
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,11 +147,11 @@ namespace UI.CardSystem.StateMachine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetupForAlbumSlot(CardData data, AlbumCardSlot slot)
|
public void SetupForAlbumSlot(CardData data, AlbumCardSlot slot)
|
||||||
{
|
{
|
||||||
SetupCard(data, "PlacedInSlotState");
|
SetupCard(data, CardStateNames.PlacedInSlot);
|
||||||
SetDraggingEnabled(false); // Cards in slots cannot be dragged out
|
SetDraggingEnabled(false); // Cards in slots cannot be dragged out
|
||||||
|
|
||||||
// Set the parent slot on the PlacedInSlotState
|
// Set the parent slot on the PlacedInSlotState
|
||||||
var placedState = GetStateComponent<States.CardPlacedInSlotState>("PlacedInSlotState");
|
var placedState = GetStateComponent<States.CardPlacedInSlotState>(CardStateNames.PlacedInSlot);
|
||||||
if (placedState != null)
|
if (placedState != null)
|
||||||
{
|
{
|
||||||
placedState.SetParentSlot(slot);
|
placedState.SetParentSlot(slot);
|
||||||
@@ -165,7 +165,7 @@ namespace UI.CardSystem.StateMachine
|
|||||||
public void SetupForAlbumPending()
|
public void SetupForAlbumPending()
|
||||||
{
|
{
|
||||||
// Start with no data; state will assign when dragged
|
// Start with no data; state will assign when dragged
|
||||||
SetupCard(null, "PendingFaceDownState");
|
SetupCard(null, CardStateNames.PendingFaceDown);
|
||||||
SetDraggingEnabled(true);
|
SetDraggingEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ namespace UI.CardSystem.StateMachine
|
|||||||
[Header("Card Data")]
|
[Header("Card Data")]
|
||||||
private CardData cardData;
|
private CardData cardData;
|
||||||
|
|
||||||
// Cached reference to AlbumViewPage (lazy-loaded)
|
// Injected dependencies
|
||||||
private AlbumViewPage _albumViewPage;
|
private AlbumViewPage albumViewPage;
|
||||||
|
|
||||||
// Public accessors
|
// Public accessors
|
||||||
public CardDisplay CardDisplay => cardDisplay;
|
public CardDisplay CardDisplay => cardDisplay;
|
||||||
@@ -30,56 +30,44 @@ namespace UI.CardSystem.StateMachine
|
|||||||
public CardData CardData => cardData;
|
public CardData CardData => cardData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the AlbumViewPage instance (cached to avoid repeated FindFirstObjectByType calls)
|
/// Get the injected AlbumViewPage (null if not set)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AlbumViewPage AlbumViewPage
|
public AlbumViewPage AlbumViewPage => albumViewPage;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_albumViewPage == null)
|
|
||||||
{
|
|
||||||
_albumViewPage = FindFirstObjectByType<AlbumViewPage>();
|
|
||||||
}
|
|
||||||
return _albumViewPage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runtime state
|
// Runtime state
|
||||||
public bool IsClickable { get; set; } = true;
|
public bool IsClickable { get; set; } = true;
|
||||||
|
|
||||||
// TODO: Move to booster-specific states - this is workflow-specific, not generic context
|
|
||||||
public bool SuppressRevealBadges { get; set; } = false; // Set by states to suppress NEW/REPEAT badges in revealed state
|
|
||||||
|
|
||||||
// Original transform data (captured on spawn for shrink animations)
|
// Original transform data (captured on spawn for shrink animations)
|
||||||
public Vector3 OriginalScale { get; private set; }
|
public Vector3 OriginalScale { get; private set; }
|
||||||
public Vector3 OriginalPosition { get; private set; }
|
public Vector3 OriginalPosition { get; private set; }
|
||||||
public Quaternion OriginalRotation { get; private set; }
|
public Quaternion OriginalRotation { get; private set; }
|
||||||
|
|
||||||
// TODO: Move to BoosterOpeningPage - reveal flow is booster-specific workflow coordination
|
|
||||||
// Single event for reveal flow completion
|
|
||||||
public event Action<CardContext> OnRevealFlowComplete;
|
|
||||||
|
|
||||||
// Generic drag event - fired when drag starts, consumers decide how to handle based on current state
|
// Generic drag event - fired when drag starts, consumers decide how to handle based on current state
|
||||||
public event Action<CardContext> OnDragStarted;
|
public event Action<CardContext> OnDragStarted;
|
||||||
|
|
||||||
// Generic drag end event - fired when drag ends, consumers decide how to handle based on current state
|
// Generic drag end event - fired when drag ends, consumers decide how to handle based on current state
|
||||||
public event Action<CardContext> OnDragEnded;
|
public event Action<CardContext> OnDragEnded;
|
||||||
|
|
||||||
// TODO: Move to booster-specific states - this tracks reveal workflow completion
|
// Booster-specific context (optional, only set for booster flow cards)
|
||||||
private bool _hasCompletedReveal = false;
|
private BoosterCardContext boosterContext;
|
||||||
public bool HasCompletedReveal => _hasCompletedReveal;
|
public BoosterCardContext BoosterContext
|
||||||
|
|
||||||
// TODO: Move to booster-specific states - workflow coordination method
|
|
||||||
// Helper method for states to signal completion
|
|
||||||
public void NotifyRevealComplete()
|
|
||||||
{
|
{
|
||||||
if (!_hasCompletedReveal)
|
get
|
||||||
{
|
{
|
||||||
_hasCompletedReveal = true;
|
if (boosterContext == null)
|
||||||
OnRevealFlowComplete?.Invoke(this);
|
boosterContext = new BoosterCardContext();
|
||||||
|
return boosterContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inject AlbumViewPage dependency
|
||||||
|
/// </summary>
|
||||||
|
public void SetAlbumViewPage(AlbumViewPage page)
|
||||||
|
{
|
||||||
|
albumViewPage = page;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper method for states/card to signal drag started
|
// Helper method for states/card to signal drag started
|
||||||
public void NotifyDragStarted()
|
public void NotifyDragStarted()
|
||||||
{
|
{
|
||||||
@@ -141,7 +129,12 @@ namespace UI.CardSystem.StateMachine
|
|||||||
public void SetupCard(CardData data)
|
public void SetupCard(CardData data)
|
||||||
{
|
{
|
||||||
cardData = data;
|
cardData = data;
|
||||||
_hasCompletedReveal = false; // Reset completion flag
|
|
||||||
|
// Reset booster context if it exists
|
||||||
|
if (boosterContext != null)
|
||||||
|
{
|
||||||
|
boosterContext.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
// Capture original transform for shrink animations.
|
// Capture original transform for shrink animations.
|
||||||
// If current scale is ~0 (pop-in staging), default to Vector3.one.
|
// If current scale is ~0 (pop-in staging), default to Vector3.one.
|
||||||
@@ -163,6 +156,30 @@ namespace UI.CardSystem.StateMachine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update card data without re-capturing original transform values.
|
||||||
|
/// Use this when assigning data to an already-initialized card (e.g., pending cards on drag).
|
||||||
|
/// This prevents changing OriginalScale when the card is already correctly sized.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateCardData(CardData data)
|
||||||
|
{
|
||||||
|
cardData = data;
|
||||||
|
|
||||||
|
// Reset booster context if it exists
|
||||||
|
if (boosterContext != null)
|
||||||
|
{
|
||||||
|
boosterContext.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't re-capture OriginalScale/Position/Rotation
|
||||||
|
// This preserves the transform values captured during initial setup
|
||||||
|
|
||||||
|
if (cardDisplay != null)
|
||||||
|
{
|
||||||
|
cardDisplay.SetupCard(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the card display component
|
/// Get the card display component
|
||||||
/// </summary>
|
/// </summary>
|
||||||
51
Assets/Scripts/CardSystem/StateMachine/BoosterCardContext.cs
Normal file
51
Assets/Scripts/CardSystem/StateMachine/BoosterCardContext.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UI.CardSystem.StateMachine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Booster-specific card context for reveal flow coordination.
|
||||||
|
/// Separated from generic CardContext to maintain single responsibility.
|
||||||
|
/// </summary>
|
||||||
|
public class BoosterCardContext
|
||||||
|
{
|
||||||
|
// Booster reveal workflow state
|
||||||
|
private bool _hasCompletedReveal = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Has this card completed its booster reveal flow?
|
||||||
|
/// </summary>
|
||||||
|
public bool HasCompletedReveal => _hasCompletedReveal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suppress NEW/REPEAT badges in revealed state
|
||||||
|
/// </summary>
|
||||||
|
public bool SuppressRevealBadges { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event fired when reveal flow is complete (card dismissed)
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnRevealFlowComplete;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signal that this card has completed its reveal flow
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyRevealComplete()
|
||||||
|
{
|
||||||
|
if (!_hasCompletedReveal)
|
||||||
|
{
|
||||||
|
_hasCompletedReveal = true;
|
||||||
|
OnRevealFlowComplete?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reset reveal state (for card reuse/pooling)
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_hasCompletedReveal = false;
|
||||||
|
SuppressRevealBadges = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 484a792a835a418bb690947b82e45da7
|
||||||
|
timeCreated: 1763421968
|
||||||
26
Assets/Scripts/CardSystem/StateMachine/CardStateNames.cs
Normal file
26
Assets/Scripts/CardSystem/StateMachine/CardStateNames.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace UI.CardSystem.StateMachine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Centralized string constants for card state names.
|
||||||
|
/// Prevents typos and provides compile-time checking for state transitions.
|
||||||
|
/// </summary>
|
||||||
|
public static class CardStateNames
|
||||||
|
{
|
||||||
|
// Booster Flow States
|
||||||
|
public const string Idle = "IdleState";
|
||||||
|
public const string EnlargedNew = "EnlargedNewState";
|
||||||
|
public const string EnlargedRepeat = "EnlargedRepeatState";
|
||||||
|
public const string EnlargedLegendaryRepeat = "EnlargedLegendaryRepeatState";
|
||||||
|
public const string Revealed = "RevealedState";
|
||||||
|
|
||||||
|
// Album Placement Flow States
|
||||||
|
public const string PendingFaceDown = "PendingFaceDownState";
|
||||||
|
public const string DraggingRevealed = "DraggingRevealedState";
|
||||||
|
public const string PlacedInSlot = "PlacedInSlotState";
|
||||||
|
public const string AlbumEnlarged = "AlbumEnlargedState";
|
||||||
|
|
||||||
|
// Generic Drag State
|
||||||
|
public const string Dragging = "DraggingState";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: de659f0ceb6f4ef6bab333d5f7de00ec
|
||||||
|
timeCreated: 1763421948
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Core.SaveLoad;
|
using Core.SaveLoad;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using AppleHills.Core.Settings;
|
using AppleHills.Core.Settings;
|
||||||
|
using Pixelplacement;
|
||||||
|
|
||||||
namespace UI.CardSystem.StateMachine.States
|
namespace UI.CardSystem.StateMachine.States
|
||||||
{
|
{
|
||||||
@@ -17,6 +18,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
private Transform _originalParent;
|
private Transform _originalParent;
|
||||||
private Vector3 _originalLocalPosition;
|
private Vector3 _originalLocalPosition;
|
||||||
private Quaternion _originalLocalRotation;
|
private Quaternion _originalLocalRotation;
|
||||||
|
private Vector3 _originalWorldPosition;
|
||||||
|
|
||||||
// Events for page to manage backdrop and reparenting
|
// Events for page to manage backdrop and reparenting
|
||||||
public event System.Action<CardAlbumEnlargedState> OnEnlargeRequested;
|
public event System.Action<CardAlbumEnlargedState> OnEnlargeRequested;
|
||||||
@@ -42,14 +44,18 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
_originalParent = _context.RootTransform.parent;
|
_originalParent = _context.RootTransform.parent;
|
||||||
_originalLocalPosition = _context.RootTransform.localPosition;
|
_originalLocalPosition = _context.RootTransform.localPosition;
|
||||||
_originalLocalRotation = _context.RootTransform.localRotation;
|
_originalLocalRotation = _context.RootTransform.localRotation;
|
||||||
|
_originalWorldPosition = _context.RootTransform.position;
|
||||||
|
|
||||||
// Notify page to show backdrop and reparent card to top layer
|
// Notify page to show backdrop and reparent card to top layer (preserve world transform)
|
||||||
OnEnlargeRequested?.Invoke(this);
|
OnEnlargeRequested?.Invoke(this);
|
||||||
|
|
||||||
// Enlarge the card using album scale setting
|
// Animate into center and scale up significantly
|
||||||
if (_context.Animator != null)
|
if (_context.Animator != null)
|
||||||
{
|
{
|
||||||
_context.Animator.PlayEnlarge(_settings.AlbumCardEnlargedScale);
|
// Move to center of enlarged container (assumes zero is centered)
|
||||||
|
_context.Animator.AnimateLocalPosition(Vector3.zero, _settings.ScaleDuration);
|
||||||
|
// Enlarge using settings-controlled scale
|
||||||
|
_context.Animator.PlayEnlarge(_settings.AlbumCardEnlargedScale, _settings.ScaleDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.Debug($"[CardAlbumEnlargedState] Card enlarged from album: {_context.CardData?.Name}");
|
Logging.Debug($"[CardAlbumEnlargedState] Card enlarged from album: {_context.CardData?.Name}");
|
||||||
@@ -57,23 +63,39 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
|
|
||||||
public void OnCardClicked(CardContext context)
|
public void OnCardClicked(CardContext context)
|
||||||
{
|
{
|
||||||
// Click to shrink back
|
// Click to shrink back (play reverse of opening animation)
|
||||||
Logging.Debug($"[CardAlbumEnlargedState] Card clicked while enlarged, shrinking back");
|
Logging.Debug($"[CardAlbumEnlargedState] Card clicked while enlarged, shrinking back");
|
||||||
|
|
||||||
// Notify page to prepare for shrink
|
// Ask page to hide backdrop (state will handle moving back & reparenting)
|
||||||
OnShrinkRequested?.Invoke(this);
|
OnShrinkRequested?.Invoke(this);
|
||||||
|
|
||||||
// Shrink animation, then transition back
|
// Animate back to original world position + shrink to original scale
|
||||||
|
float duration = _settings.ScaleDuration;
|
||||||
|
|
||||||
|
// Move using world-space tween so we land exactly on the original position
|
||||||
|
Tween.Position(context.RootTransform, _originalWorldPosition, duration, 0f, Tween.EaseInOut);
|
||||||
|
|
||||||
if (context.Animator != null)
|
if (context.Animator != null)
|
||||||
{
|
{
|
||||||
context.Animator.PlayShrink(_originalScale, onComplete: () =>
|
context.Animator.PlayShrink(_originalScale, duration, onComplete: () =>
|
||||||
{
|
{
|
||||||
context.StateMachine.ChangeState("PlacedInSlotState");
|
// Reparent back to original hierarchy and restore local transform
|
||||||
|
context.RootTransform.SetParent(_originalParent, true);
|
||||||
|
context.RootTransform.localPosition = _originalLocalPosition;
|
||||||
|
context.RootTransform.localRotation = _originalLocalRotation;
|
||||||
|
|
||||||
|
// Transition back to placed state
|
||||||
|
context.StateMachine.ChangeState(CardStateNames.PlacedInSlot);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.StateMachine.ChangeState("PlacedInSlotState");
|
// Fallback if no animator: snap back
|
||||||
|
context.RootTransform.position = _originalWorldPosition;
|
||||||
|
context.RootTransform.SetParent(_originalParent, true);
|
||||||
|
context.RootTransform.localPosition = _originalLocalPosition;
|
||||||
|
context.RootTransform.localRotation = _originalLocalRotation;
|
||||||
|
context.StateMachine.ChangeState(CardStateNames.PlacedInSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,4 +124,3 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
Logging.Warning("[CardDraggingRevealedState] No target slot set - cannot place card");
|
Logging.Warning("[CardDraggingRevealedState] No target slot set - cannot place card");
|
||||||
// Return to corner
|
// Return to corner
|
||||||
_context.StateMachine.ChangeState("PendingFaceDownState");
|
_context.StateMachine.ChangeState(CardStateNames.PendingFaceDown);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
var albumPage = _context.AlbumViewPage;
|
var albumPage = _context.AlbumViewPage;
|
||||||
if (albumPage == null)
|
if (albumPage == null)
|
||||||
{
|
{
|
||||||
Logging.Warning("[CardDraggingRevealedState] AlbumViewPage not found - placing immediately");
|
Logging.Warning("[CardDraggingRevealedState] AlbumViewPage not injected - placing immediately");
|
||||||
TransitionToPlacement(_context);
|
TransitionToPlacement(_context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
var card = ctx.GetComponent<Card>();
|
var card = ctx.GetComponent<Card>();
|
||||||
if (card != null)
|
if (card != null)
|
||||||
{
|
{
|
||||||
var placedState = card.GetStateComponent<CardPlacedInSlotState>("PlacedInSlotState");
|
var placedState = card.GetStateComponent<CardPlacedInSlotState>(CardStateNames.PlacedInSlot);
|
||||||
if (placedState != null)
|
if (placedState != null)
|
||||||
{
|
{
|
||||||
placedState.SetPlacementInfo(_targetSlot);
|
placedState.SetPlacementInfo(_targetSlot);
|
||||||
@@ -155,7 +155,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
|
|
||||||
// Transition to PlacedInSlotState
|
// Transition to PlacedInSlotState
|
||||||
// The state will handle animation and finalization in OnEnterState
|
// The state will handle animation and finalization in OnEnterState
|
||||||
ctx.StateMachine.ChangeState("PlacedInSlotState");
|
ctx.StateMachine.ChangeState(CardStateNames.PlacedInSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
@@ -58,13 +58,13 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
context.Animator.PlayShrink(context.OriginalScale, onComplete: () =>
|
context.Animator.PlayShrink(context.OriginalScale, onComplete: () =>
|
||||||
{
|
{
|
||||||
context.StateMachine.ChangeState("RevealedState");
|
context.StateMachine.ChangeState(CardStateNames.Revealed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fallback if no animator
|
// Fallback if no animator
|
||||||
context.StateMachine.ChangeState("RevealedState");
|
context.StateMachine.ChangeState(CardStateNames.Revealed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +162,12 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
if (newRarity == CardRarity.Legendary)
|
if (newRarity == CardRarity.Legendary)
|
||||||
{
|
{
|
||||||
// Show special enlarged legendary presentation, await click to shrink to revealed
|
// Show special enlarged legendary presentation, await click to shrink to revealed
|
||||||
_context.StateMachine.ChangeState("EnlargedLegendaryRepeatState");
|
_context.StateMachine.ChangeState(CardStateNames.EnlargedLegendaryRepeat);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Treat as NEW at higher rarity (enlarged with NEW visuals handled there)
|
// Treat as NEW at higher rarity (enlarged with NEW visuals handled there)
|
||||||
_context.StateMachine.ChangeState("EnlargedNewState");
|
_context.StateMachine.ChangeState(CardStateNames.EnlargedNew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
|
|
||||||
// Transition to EnlargedNewState (card is already enlarged, will show NEW badge)
|
// Transition to EnlargedNewState (card is already enlarged, will show NEW badge)
|
||||||
// State will query fresh collection data to determine if truly new
|
// State will query fresh collection data to determine if truly new
|
||||||
_context.StateMachine.ChangeState("EnlargedNewState");
|
_context.StateMachine.ChangeState(CardStateNames.EnlargedNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnCardClicked(CardContext context)
|
public void OnCardClicked(CardContext context)
|
||||||
@@ -196,7 +196,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
context.Animator.PlayShrink(context.OriginalScale, onComplete: () =>
|
context.Animator.PlayShrink(context.OriginalScale, onComplete: () =>
|
||||||
{
|
{
|
||||||
context.StateMachine.ChangeState("RevealedState");
|
context.StateMachine.ChangeState(CardStateNames.Revealed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -121,7 +121,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
if (isNew)
|
if (isNew)
|
||||||
{
|
{
|
||||||
// New card - show "NEW" badge and enlarge
|
// New card - show "NEW" badge and enlarge
|
||||||
_context.StateMachine.ChangeState("EnlargedNewState");
|
_context.StateMachine.ChangeState(CardStateNames.EnlargedNew);
|
||||||
}
|
}
|
||||||
else if (_context.CardData != null && _context.CardData.Rarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
|
else if (_context.CardData != null && _context.CardData.Rarity == AppleHills.Data.CardSystem.CardRarity.Legendary)
|
||||||
{
|
{
|
||||||
@@ -131,12 +131,12 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(_context.CardData);
|
Data.CardSystem.CardSystemManager.Instance.AddCardToInventoryDelayed(_context.CardData);
|
||||||
}
|
}
|
||||||
_context.StateMachine.ChangeState("RevealedState");
|
_context.StateMachine.ChangeState(CardStateNames.Revealed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Repeat card - show progress toward upgrade
|
// Repeat card - show progress toward upgrade
|
||||||
_context.StateMachine.ChangeState("EnlargedRepeatState");
|
_context.StateMachine.ChangeState(CardStateNames.EnlargedRepeat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +32,9 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
_targetSlot = null;
|
_targetSlot = null;
|
||||||
_dragEndedDuringFlip = false;
|
_dragEndedDuringFlip = false;
|
||||||
|
|
||||||
|
// Reset scale to normal (in case transitioning from scaled state)
|
||||||
|
_context.RootTransform.localScale = Vector3.one;
|
||||||
|
|
||||||
// Show card back, hide card front
|
// Show card back, hide card front
|
||||||
if (cardBackVisual != null)
|
if (cardBackVisual != null)
|
||||||
{
|
{
|
||||||
@@ -53,11 +56,11 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
if (_isFlipping) return true; // Already handling
|
if (_isFlipping) return true; // Already handling
|
||||||
|
|
||||||
// Step 1: Find AlbumViewPage
|
// Get AlbumViewPage from context (injected dependency)
|
||||||
AlbumViewPage albumPage = Object.FindFirstObjectByType<AlbumViewPage>();
|
var albumPage = context.AlbumViewPage;
|
||||||
if (albumPage == null)
|
if (albumPage == null)
|
||||||
{
|
{
|
||||||
Logging.Warning("[CardPendingFaceDownState] AlbumViewPage not found!");
|
Logging.Warning("[CardPendingFaceDownState] AlbumViewPage not injected!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +73,9 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Apply card data to context
|
// Step 3: Apply card data to context
|
||||||
context.SetupCard(cardData);
|
// Use UpdateCardData instead of SetupCard to preserve OriginalScale
|
||||||
|
// (card was already initialized with correct scale in SpawnCardInSlot)
|
||||||
|
context.UpdateCardData(cardData);
|
||||||
Logging.Debug($"[CardPendingFaceDownState] Assigned card data: {cardData.Name} ({cardData.Zone})");
|
Logging.Debug($"[CardPendingFaceDownState] Assigned card data: {cardData.Name} ({cardData.Zone})");
|
||||||
|
|
||||||
// Step 4: Ask AlbumViewPage for target slot
|
// Step 4: Ask AlbumViewPage for target slot
|
||||||
@@ -138,11 +143,11 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
private void OnFlipComplete()
|
private void OnFlipComplete()
|
||||||
{
|
{
|
||||||
// Transition to dragging revealed state
|
// Transition to dragging revealed state
|
||||||
// Pass target slot to next state (it will query AlbumViewPage for flip status)
|
// Pass target slot to next state (it will query AlbumService for flip status)
|
||||||
var card = _context.GetComponent<Card>();
|
var card = _context.GetComponent<Card>();
|
||||||
if (card != null)
|
if (card != null)
|
||||||
{
|
{
|
||||||
var draggingState = card.GetStateComponent<CardDraggingRevealedState>("DraggingRevealedState");
|
var draggingState = card.GetStateComponent<CardDraggingRevealedState>(CardStateNames.DraggingRevealed);
|
||||||
if (draggingState != null)
|
if (draggingState != null)
|
||||||
{
|
{
|
||||||
draggingState.SetTargetSlot(_targetSlot);
|
draggingState.SetTargetSlot(_targetSlot);
|
||||||
@@ -156,7 +161,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.StateMachine.ChangeState("DraggingRevealedState");
|
_context.StateMachine.ChangeState(CardStateNames.DraggingRevealed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Core;
|
using Core;
|
||||||
using Core.SaveLoad;
|
using Core.SaveLoad;
|
||||||
using Data.CardSystem;
|
using Data.CardSystem;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -143,7 +143,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
{
|
{
|
||||||
// Click to enlarge when in album
|
// Click to enlarge when in album
|
||||||
Logging.Debug($"[CardPlacedInSlotState] Card clicked in slot, transitioning to enlarged state");
|
Logging.Debug($"[CardPlacedInSlotState] Card clicked in slot, transitioning to enlarged state");
|
||||||
context.StateMachine.ChangeState("AlbumEnlargedState");
|
context.StateMachine.ChangeState(CardStateNames.AlbumEnlarged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show appropriate idle badge unless suppressed
|
// Show appropriate idle badge unless suppressed
|
||||||
if (_context.SuppressRevealBadges)
|
if (_context.BoosterContext.SuppressRevealBadges)
|
||||||
{
|
{
|
||||||
if (newCardIdleBadge != null) newCardIdleBadge.SetActive(false);
|
if (newCardIdleBadge != null) newCardIdleBadge.SetActive(false);
|
||||||
if (repeatCardIdleBadge != null) repeatCardIdleBadge.SetActive(false);
|
if (repeatCardIdleBadge != null) repeatCardIdleBadge.SetActive(false);
|
||||||
@@ -61,7 +61,7 @@ namespace UI.CardSystem.StateMachine.States
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fire reveal flow complete event (signals booster page that this card is done)
|
// Fire reveal flow complete event (signals booster page that this card is done)
|
||||||
_context.NotifyRevealComplete();
|
_context.BoosterContext.NotifyRevealComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
@@ -54,7 +54,8 @@ namespace UI.CardSystem.Testing
|
|||||||
// Subscribe to card events (new simplified event model)
|
// Subscribe to card events (new simplified event model)
|
||||||
if (_cardContext != null)
|
if (_cardContext != null)
|
||||||
{
|
{
|
||||||
_cardContext.OnRevealFlowComplete += OnCardRevealFlowComplete;
|
// TODO: FIX
|
||||||
|
// _cardContext.OnRevealFlowComplete += OnCardRevealFlowComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to drag events to ensure card snaps back when released
|
// Subscribe to drag events to ensure card snaps back when released
|
||||||
@@ -438,7 +439,8 @@ namespace UI.CardSystem.Testing
|
|||||||
// Unsubscribe from events
|
// Unsubscribe from events
|
||||||
if (_cardContext != null)
|
if (_cardContext != null)
|
||||||
{
|
{
|
||||||
_cardContext.OnRevealFlowComplete -= OnCardRevealFlowComplete;
|
// TODO: FIX
|
||||||
|
// _cardContext.OnRevealFlowComplete -= OnCardRevealFlowComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testCard != null)
|
if (testCard != null)
|
||||||
3
Assets/Scripts/CardSystem/UI.meta
Normal file
3
Assets/Scripts/CardSystem/UI.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2a6295f642a94601ada9c21dc400d180
|
||||||
|
timeCreated: 1763454480
|
||||||
3
Assets/Scripts/CardSystem/UI/Component.meta
Normal file
3
Assets/Scripts/CardSystem/UI/Component.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7cb791415c884e97ac181816424200e4
|
||||||
|
timeCreated: 1763454497
|
||||||
3
Assets/Scripts/CardSystem/UI/Pages.meta
Normal file
3
Assets/Scripts/CardSystem/UI/Pages.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c548995da9c746d1916b79304734c1c9
|
||||||
|
timeCreated: 1763454486
|
||||||
491
Assets/Scripts/CardSystem/UI/Pages/AlbumViewPage.cs
Normal file
491
Assets/Scripts/CardSystem/UI/Pages/AlbumViewPage.cs
Normal file
@@ -0,0 +1,491 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using AppleHills.Data.CardSystem;
|
||||||
|
using Core;
|
||||||
|
using Data.CardSystem;
|
||||||
|
using Pixelplacement;
|
||||||
|
using UI.Core;
|
||||||
|
using UI.DragAndDrop.Core;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.Serialization;
|
||||||
|
|
||||||
|
namespace UI.CardSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UI page for viewing the player's card collection in an album.
|
||||||
|
/// Manages booster pack button visibility and opening flow.
|
||||||
|
/// </summary>
|
||||||
|
public class AlbumViewPage : UIPage
|
||||||
|
{
|
||||||
|
[Header("UI References")]
|
||||||
|
[SerializeField] private CanvasGroup canvasGroup;
|
||||||
|
[SerializeField] private Button exitButton;
|
||||||
|
[SerializeField] private BookCurlPro.BookPro book;
|
||||||
|
|
||||||
|
[Header("Zone Navigation")]
|
||||||
|
[SerializeField] private Transform tabContainer; // Container holding all BookTabButton children
|
||||||
|
private BookTabButton[] _zoneTabs; // Discovered zone tab buttons
|
||||||
|
|
||||||
|
[Header("Album Card Reveal")]
|
||||||
|
[SerializeField] private SlotContainer bottomRightSlots;
|
||||||
|
[FormerlySerializedAs("albumCardPlacementPrefab")]
|
||||||
|
[SerializeField] private GameObject cardPrefab; // New Card prefab for placement
|
||||||
|
|
||||||
|
[Header("Card Enlarge System")]
|
||||||
|
[SerializeField] private GameObject cardEnlargedBackdrop; // Backdrop to block interactions
|
||||||
|
[SerializeField] private Transform cardEnlargedContainer; // Container for enlarged cards (sits above backdrop)
|
||||||
|
|
||||||
|
[Header("Booster Pack UI")]
|
||||||
|
[SerializeField] private GameObject[] boosterPackButtons;
|
||||||
|
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
|
||||||
|
|
||||||
|
private Input.InputMode _previousInputMode;
|
||||||
|
|
||||||
|
// Controllers: Lazy-initialized services (auto-created on first use)
|
||||||
|
private CornerCardManager _cornerCardManager;
|
||||||
|
private CornerCardManager CornerCards => _cornerCardManager ??= new CornerCardManager(
|
||||||
|
bottomRightSlots,
|
||||||
|
cardPrefab,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
private AlbumNavigationService _navigationService;
|
||||||
|
private AlbumNavigationService Navigation => _navigationService ??= new AlbumNavigationService(
|
||||||
|
book,
|
||||||
|
_zoneTabs
|
||||||
|
);
|
||||||
|
|
||||||
|
private CardEnlargeController _enlargeController;
|
||||||
|
private CardEnlargeController Enlarge => _enlargeController ??= new CardEnlargeController(
|
||||||
|
cardEnlargedBackdrop,
|
||||||
|
cardEnlargedContainer
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query method: Check if the book is currently flipping to a page.
|
||||||
|
/// Used by card states to know if they should wait before placing.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPageFlipping => Navigation.IsPageFlipping;
|
||||||
|
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
// Discover zone tabs from container
|
||||||
|
DiscoverZoneTabs();
|
||||||
|
|
||||||
|
// Make sure we have a CanvasGroup for transitions
|
||||||
|
if (canvasGroup == null)
|
||||||
|
canvasGroup = GetComponent<CanvasGroup>();
|
||||||
|
if (canvasGroup == null)
|
||||||
|
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||||
|
|
||||||
|
// Hide backdrop initially
|
||||||
|
if (cardEnlargedBackdrop != null)
|
||||||
|
{
|
||||||
|
cardEnlargedBackdrop.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up exit button
|
||||||
|
if (exitButton != null)
|
||||||
|
{
|
||||||
|
exitButton.onClick.AddListener(OnExitButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up booster pack button listeners
|
||||||
|
SetupBoosterButtonListeners();
|
||||||
|
|
||||||
|
// Subscribe to book page flip events
|
||||||
|
if (book != null)
|
||||||
|
{
|
||||||
|
book.OnFlip.AddListener(OnPageFlipped);
|
||||||
|
Logging.Debug("[AlbumViewPage] Subscribed to book.OnFlip event");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Warning("[AlbumViewPage] Book reference is null, cannot subscribe to OnFlip event!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to CardSystemManager events (managers are guaranteed to be initialized)
|
||||||
|
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();
|
||||||
|
UpdateBoosterButtons(initialCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI pages should start disabled
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Discover all BookTabButton components from the tab container
|
||||||
|
/// </summary>
|
||||||
|
private void DiscoverZoneTabs()
|
||||||
|
{
|
||||||
|
if (tabContainer == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("[AlbumViewPage] Tab container is not assigned! Cannot discover zone tabs.");
|
||||||
|
_zoneTabs = new BookTabButton[0];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all BookTabButton components from children
|
||||||
|
_zoneTabs = tabContainer.GetComponentsInChildren<BookTabButton>(includeInactive: false);
|
||||||
|
|
||||||
|
if (_zoneTabs == null || _zoneTabs.Length == 0)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[AlbumViewPage] No BookTabButton components found in tab container '{tabContainer.name}'!");
|
||||||
|
_zoneTabs = new BookTabButton[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Debug($"[AlbumViewPage] Discovered {_zoneTabs.Length} zone tabs from container '{tabContainer.name}'");
|
||||||
|
foreach (var tab in _zoneTabs)
|
||||||
|
{
|
||||||
|
Logging.Debug($" - Tab: {tab.name}, Zone: {tab.Zone}, TargetPage: {tab.TargetPage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupBoosterButtonListeners()
|
||||||
|
{
|
||||||
|
if (boosterPackButtons == null) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < boosterPackButtons.Length; i++)
|
||||||
|
{
|
||||||
|
if (boosterPackButtons[i] == null) continue;
|
||||||
|
|
||||||
|
|
||||||
|
Button button = boosterPackButtons[i].GetComponent<Button>();
|
||||||
|
if (button != null)
|
||||||
|
{
|
||||||
|
button.onClick.AddListener(OnBoosterButtonClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void OnManagedDestroy()
|
||||||
|
{
|
||||||
|
// Unsubscribe from book events
|
||||||
|
if (book != null)
|
||||||
|
{
|
||||||
|
book.OnFlip.RemoveListener(OnPageFlipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsubscribe from CardSystemManager
|
||||||
|
if (CardSystemManager.Instance != null)
|
||||||
|
{
|
||||||
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up exit button
|
||||||
|
if (exitButton != null)
|
||||||
|
{
|
||||||
|
exitButton.onClick.RemoveListener(OnExitButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up booster button listeners
|
||||||
|
if (boosterPackButtons != null)
|
||||||
|
{
|
||||||
|
foreach (var buttonObj in boosterPackButtons)
|
||||||
|
{
|
||||||
|
if (buttonObj == null) continue;
|
||||||
|
|
||||||
|
Button button = buttonObj.GetComponent<Button>();
|
||||||
|
if (button != null)
|
||||||
|
{
|
||||||
|
button.onClick.RemoveListener(OnBoosterButtonClicked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up pending corner cards
|
||||||
|
CleanupPendingCornerCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExitButtonClicked()
|
||||||
|
{
|
||||||
|
if (book != null && book.CurrentPaper != 1)
|
||||||
|
{
|
||||||
|
// Not on page 0, flip to page 0 first
|
||||||
|
BookCurlPro.AutoFlip autoFlip = book.GetComponent<BookCurlPro.AutoFlip>();
|
||||||
|
if (autoFlip == null)
|
||||||
|
{
|
||||||
|
autoFlip = book.gameObject.AddComponent<BookCurlPro.AutoFlip>();
|
||||||
|
}
|
||||||
|
|
||||||
|
autoFlip.enabled = true;
|
||||||
|
autoFlip.StartFlipping(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Already on page 0 or no book reference, exit
|
||||||
|
// Restore input mode before popping
|
||||||
|
if (Input.InputManager.Instance != null)
|
||||||
|
{
|
||||||
|
Input.InputManager.Instance.SetInputMode(_previousInputMode);
|
||||||
|
Logging.Debug($"[AlbumViewPage] Restored input mode to {_previousInputMode} on exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UIPageController.Instance != null)
|
||||||
|
{
|
||||||
|
UIPageController.Instance.PopPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBoosterCountChanged(int newCount)
|
||||||
|
{
|
||||||
|
UpdateBoosterButtons(newCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBoosterButtons(int boosterCount)
|
||||||
|
{
|
||||||
|
if (boosterPackButtons == null || boosterPackButtons.Length == 0) return;
|
||||||
|
|
||||||
|
int visibleCount = Mathf.Min(boosterCount, boosterPackButtons.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < boosterPackButtons.Length; i++)
|
||||||
|
{
|
||||||
|
if (boosterPackButtons[i] != null)
|
||||||
|
{
|
||||||
|
boosterPackButtons[i].SetActive(i < visibleCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBoosterButtonClicked()
|
||||||
|
{
|
||||||
|
if (boosterOpeningPage != null && UIPageController.Instance != null)
|
||||||
|
{
|
||||||
|
// Pass current booster count to the opening page
|
||||||
|
int boosterCount = CardSystemManager.Instance?.GetBoosterPackCount() ?? 0;
|
||||||
|
boosterOpeningPage.SetAvailableBoosterCount(boosterCount);
|
||||||
|
|
||||||
|
UIPageController.Instance.PushPage(boosterOpeningPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void TransitionIn()
|
||||||
|
{
|
||||||
|
// Only store and switch input mode if this is the first time entering
|
||||||
|
if (Input.InputManager.Instance != null)
|
||||||
|
{
|
||||||
|
// Store the current input mode before switching
|
||||||
|
_previousInputMode = Input.InputMode.GameAndUI;
|
||||||
|
Input.InputManager.Instance.SetInputMode(Input.InputMode.UI);
|
||||||
|
Logging.Debug("[AlbumViewPage] Switched to UI-only input mode on first entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only spawn pending cards if we're already on an album page (not the menu)
|
||||||
|
if (IsInAlbumProper())
|
||||||
|
{
|
||||||
|
Logging.Debug("[AlbumViewPage] Opening directly to album page - spawning cards immediately");
|
||||||
|
SpawnPendingCornerCards();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Debug("[AlbumViewPage] Opening to menu page - cards will spawn when entering album");
|
||||||
|
}
|
||||||
|
|
||||||
|
base.TransitionIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void TransitionOut()
|
||||||
|
{
|
||||||
|
// Clean up active pending cards to prevent duplicates on next opening
|
||||||
|
CleanupPendingCornerCards();
|
||||||
|
|
||||||
|
// Don't restore input mode here - only restore when actually exiting (in OnExitButtonClicked)
|
||||||
|
base.TransitionOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DoTransitionIn(System.Action onComplete)
|
||||||
|
{
|
||||||
|
// Simple fade in animation
|
||||||
|
if (canvasGroup != null)
|
||||||
|
{
|
||||||
|
canvasGroup.alpha = 0f;
|
||||||
|
Tween.Value(0f, 1f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback if no CanvasGroup
|
||||||
|
onComplete?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DoTransitionOut(System.Action onComplete)
|
||||||
|
{
|
||||||
|
// Clean up any enlarged card state before closing
|
||||||
|
CleanupEnlargedCardState();
|
||||||
|
|
||||||
|
// Simple fade out animation
|
||||||
|
if (canvasGroup != null)
|
||||||
|
{
|
||||||
|
Tween.Value(canvasGroup.alpha, 0f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback if no CanvasGroup
|
||||||
|
onComplete?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up enlarged card state when closing the album
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupEnlargedCardState()
|
||||||
|
{
|
||||||
|
Enlarge.CleanupEnlargedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if we're currently viewing the album proper (not the menu page)
|
||||||
|
/// </summary>
|
||||||
|
private bool IsInAlbumProper()
|
||||||
|
{
|
||||||
|
return Navigation.IsInAlbumProper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when book page flips - show/hide pending cards based on whether we're in the album proper
|
||||||
|
/// </summary>
|
||||||
|
private void OnPageFlipped()
|
||||||
|
{
|
||||||
|
bool isInAlbum = IsInAlbumProper();
|
||||||
|
if (isInAlbum && CornerCards.PendingCards.Count == 0)
|
||||||
|
{
|
||||||
|
// Entering album proper and no cards spawned yet - spawn them with animation
|
||||||
|
Logging.Debug("[AlbumViewPage] Entering album proper - spawning pending cards with animation");
|
||||||
|
SpawnPendingCornerCards();
|
||||||
|
}
|
||||||
|
else if (!isInAlbum && CornerCards.PendingCards.Count > 0)
|
||||||
|
{
|
||||||
|
// Returning to menu page - cleanup cards
|
||||||
|
Logging.Debug("[AlbumViewPage] Returning to menu page - cleaning up pending cards");
|
||||||
|
CleanupPendingCornerCards();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Debug($"[AlbumViewPage] Page flipped but no card state change needed (already in correct state)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Card Enlarge System (Album Slots)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscribe to a placed card's enlarged state events to manage backdrop and reparenting.
|
||||||
|
/// Called by AlbumCardSlot when it spawns an owned card in a slot.
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterCardInAlbum(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
Enlarge.RegisterCard(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterCardInAlbum(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
Enlarge.UnregisterCard(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SpawnPendingCornerCards()
|
||||||
|
{
|
||||||
|
CornerCards.SpawnCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupPendingCornerCards()
|
||||||
|
{
|
||||||
|
CornerCards.CleanupAllCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Query Methods for Card States (Data Providers)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query method: Get card data for a pending slot.
|
||||||
|
/// Called by PendingFaceDownState when drag starts.
|
||||||
|
/// IMPORTANT: This removes the card from pending list immediately, then rebuilds corner.
|
||||||
|
/// </summary>
|
||||||
|
public CardData GetCardForPendingSlot()
|
||||||
|
{
|
||||||
|
return CornerCards.GetSmartSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Query method: Get target slot for a card.
|
||||||
|
/// Called by PendingFaceDownState to find where card should go.
|
||||||
|
/// </summary>
|
||||||
|
public AlbumCardSlot GetTargetSlotForCard(CardData cardData)
|
||||||
|
{
|
||||||
|
if (cardData == null) return null;
|
||||||
|
|
||||||
|
var allSlots = FindObjectsByType<AlbumCardSlot>(FindObjectsSortMode.None);
|
||||||
|
|
||||||
|
foreach (var slot in allSlots)
|
||||||
|
{
|
||||||
|
if (slot.TargetCardDefinition != null &&
|
||||||
|
slot.TargetCardDefinition.Id == cardData.DefinitionId)
|
||||||
|
{
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service method: Navigate to the page for a specific card.
|
||||||
|
/// Called by PendingFaceDownState to flip book to correct zone page.
|
||||||
|
/// </summary>
|
||||||
|
public void NavigateToCardPage(CardData cardData, System.Action onComplete)
|
||||||
|
{
|
||||||
|
Navigation.NavigateToCardPage(cardData, onComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notify that a card has been placed (for cleanup).
|
||||||
|
/// Called by PlacedInSlotState after placement is complete.
|
||||||
|
/// </summary>
|
||||||
|
public void NotifyCardPlaced(StateMachine.Card card)
|
||||||
|
{
|
||||||
|
// Delegate to corner card manager for tracking removal
|
||||||
|
CornerCards.NotifyCardPlaced(card);
|
||||||
|
|
||||||
|
// Register for enlarge/shrink functionality
|
||||||
|
RegisterCardInAlbum(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
|
||||||
|
public List<string> GetDefinitionsOnCurrentPage()
|
||||||
|
{
|
||||||
|
return Navigation.GetDefinitionsOnCurrentPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -584,8 +584,8 @@ namespace UI.CardSystem
|
|||||||
// Subscribe to CardDisplay click for selection
|
// Subscribe to CardDisplay click for selection
|
||||||
context.CardDisplay.OnCardClicked += (_) => OnCardClicked(card);
|
context.CardDisplay.OnCardClicked += (_) => OnCardClicked(card);
|
||||||
|
|
||||||
// Subscribe to reveal flow complete event
|
// Subscribe to reveal flow complete event from booster context
|
||||||
context.OnRevealFlowComplete += (ctx) => OnCardRevealComplete(card);
|
context.BoosterContext.OnRevealFlowComplete += () => OnCardRevealComplete(card);
|
||||||
|
|
||||||
// Track the card
|
// Track the card
|
||||||
_currentCards.Add(card);
|
_currentCards.Add(card);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user