2025-11-07 01:51:03 +01:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using AppleHills.Data.CardSystem;
|
2025-11-08 22:16:43 +01:00
|
|
|
|
using Core;
|
2025-11-06 10:10:54 +01:00
|
|
|
|
using Data.CardSystem;
|
|
|
|
|
|
using Pixelplacement;
|
2025-10-21 12:10:16 +02:00
|
|
|
|
using UI.Core;
|
2025-11-07 01:51:03 +01:00
|
|
|
|
using UI.DragAndDrop.Core;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
2025-11-16 20:35:54 +01:00
|
|
|
|
using UnityEngine.Serialization;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
|
2025-10-21 12:10:16 +02:00
|
|
|
|
namespace UI.CardSystem
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2025-10-20 08:32:57 +02:00
|
|
|
|
/// UI page for viewing the player's card collection in an album.
|
2025-11-06 10:10:54 +01:00
|
|
|
|
/// Manages booster pack button visibility and opening flow.
|
2025-10-10 14:31:51 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class AlbumViewPage : UIPage
|
|
|
|
|
|
{
|
2025-11-06 10:10:54 +01:00
|
|
|
|
[Header("UI References")]
|
2025-10-15 09:22:13 +02:00
|
|
|
|
[SerializeField] private CanvasGroup canvasGroup;
|
2025-11-06 01:25:13 +01:00
|
|
|
|
[SerializeField] private Button exitButton;
|
|
|
|
|
|
[SerializeField] private BookCurlPro.BookPro book;
|
2025-11-06 10:10:54 +01:00
|
|
|
|
|
2025-11-07 01:51:03 +01:00
|
|
|
|
[Header("Zone Navigation")]
|
2025-11-10 12:29:17 +01:00
|
|
|
|
[SerializeField] private Transform tabContainer; // Container holding all BookTabButton children
|
2025-11-17 14:30:07 +01:00
|
|
|
|
private BookTabButton[] _zoneTabs; // Discovered zone tab buttons
|
2025-11-07 01:51:03 +01:00
|
|
|
|
|
|
|
|
|
|
[Header("Album Card Reveal")]
|
|
|
|
|
|
[SerializeField] private SlotContainer bottomRightSlots;
|
2025-11-16 20:35:54 +01:00
|
|
|
|
[FormerlySerializedAs("albumCardPlacementPrefab")]
|
|
|
|
|
|
[SerializeField] private GameObject cardPrefab; // New Card prefab for placement
|
2025-11-07 11:24:19 +01:00
|
|
|
|
|
|
|
|
|
|
[Header("Card Enlarge System")]
|
|
|
|
|
|
[SerializeField] private GameObject cardEnlargedBackdrop; // Backdrop to block interactions
|
|
|
|
|
|
[SerializeField] private Transform cardEnlargedContainer; // Container for enlarged cards (sits above backdrop)
|
2025-11-07 01:51:03 +01:00
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
[Header("Booster Pack UI")]
|
|
|
|
|
|
[SerializeField] private GameObject[] boosterPackButtons;
|
|
|
|
|
|
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
|
2025-11-06 13:18:39 +01:00
|
|
|
|
|
|
|
|
|
|
private Input.InputMode _previousInputMode;
|
2025-11-17 08:39:41 +01:00
|
|
|
|
private List<StateMachine.Card> _pendingCornerCards = new List<StateMachine.Card>();
|
2025-11-17 14:30:07 +01:00
|
|
|
|
private const int MaxPendingCorner = 3;
|
|
|
|
|
|
|
|
|
|
|
|
// Pending card placement coordination
|
|
|
|
|
|
private StateMachine.Card _pendingPlacementCard;
|
|
|
|
|
|
private bool _waitingForPageFlip;
|
|
|
|
|
|
private bool _cardDragReleased;
|
2025-11-17 08:39:41 +01:00
|
|
|
|
|
2025-11-11 08:48:29 +00:00
|
|
|
|
internal override void OnManagedStart()
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-11-10 12:29:17 +01:00
|
|
|
|
// Discover zone tabs from container
|
|
|
|
|
|
DiscoverZoneTabs();
|
|
|
|
|
|
|
2025-10-15 09:22:13 +02:00
|
|
|
|
// Make sure we have a CanvasGroup for transitions
|
|
|
|
|
|
if (canvasGroup == null)
|
|
|
|
|
|
canvasGroup = GetComponent<CanvasGroup>();
|
|
|
|
|
|
if (canvasGroup == null)
|
|
|
|
|
|
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
2025-11-10 12:41:28 +01:00
|
|
|
|
|
2025-11-07 11:24:19 +01:00
|
|
|
|
// Hide backdrop initially
|
|
|
|
|
|
if (cardEnlargedBackdrop != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardEnlargedBackdrop.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 01:25:13 +01:00
|
|
|
|
// Set up exit button
|
|
|
|
|
|
if (exitButton != null)
|
2025-10-20 10:29:22 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
exitButton.onClick.AddListener(OnExitButtonClicked);
|
2025-10-20 10:29:22 +02:00
|
|
|
|
}
|
2025-10-15 09:22:13 +02:00
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
// Set up booster pack button listeners
|
|
|
|
|
|
SetupBoosterButtonListeners();
|
|
|
|
|
|
|
2025-11-10 12:41:28 +01:00
|
|
|
|
// Subscribe to book page flip events
|
|
|
|
|
|
if (book != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
book.OnFlip.AddListener(OnPageFlipped);
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Subscribed to book.OnFlip event");
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Warning("[AlbumViewPage] Book reference is null, cannot subscribe to OnFlip event!");
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-07 15:38:31 +00:00
|
|
|
|
// Subscribe to CardSystemManager events (managers are guaranteed to be initialized)
|
2025-11-06 10:10:54 +01:00
|
|
|
|
if (CardSystemManager.Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
2025-11-07 01:51:03 +01:00
|
|
|
|
// NOTE: OnPendingCardAdded is subscribed in TransitionIn, not here
|
|
|
|
|
|
// This prevents spawning cards when page is not active
|
2025-11-06 10:10:54 +01:00
|
|
|
|
|
|
|
|
|
|
// Update initial button visibility
|
|
|
|
|
|
int initialCount = CardSystemManager.Instance.GetBoosterPackCount();
|
|
|
|
|
|
UpdateBoosterButtons(initialCount);
|
|
|
|
|
|
}
|
2025-11-07 15:38:31 +00:00
|
|
|
|
|
|
|
|
|
|
// UI pages should start disabled
|
|
|
|
|
|
gameObject.SetActive(false);
|
2025-11-06 10:10:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 12:29:17 +01:00
|
|
|
|
/// <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.");
|
2025-11-17 14:30:07 +01:00
|
|
|
|
_zoneTabs = new BookTabButton[0];
|
2025-11-10 12:29:17 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get all BookTabButton components from children
|
2025-11-17 14:30:07 +01:00
|
|
|
|
_zoneTabs = tabContainer.GetComponentsInChildren<BookTabButton>(includeInactive: false);
|
2025-11-10 12:29:17 +01:00
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
if (_zoneTabs == null || _zoneTabs.Length == 0)
|
2025-11-10 12:29:17 +01:00
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Warning($"[AlbumViewPage] No BookTabButton components found in tab container '{tabContainer.name}'!");
|
2025-11-17 14:30:07 +01:00
|
|
|
|
_zoneTabs = new BookTabButton[0];
|
2025-11-10 12:29:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-11-17 14:30:07 +01:00
|
|
|
|
Logging.Debug($"[AlbumViewPage] Discovered {_zoneTabs.Length} zone tabs from container '{tabContainer.name}'");
|
|
|
|
|
|
foreach (var tab in _zoneTabs)
|
2025-11-10 12:29:17 +01:00
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug($" - Tab: {tab.name}, Zone: {tab.Zone}, TargetPage: {tab.TargetPage}");
|
2025-11-10 12:29:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
private void SetupBoosterButtonListeners()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (boosterPackButtons == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < boosterPackButtons.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (boosterPackButtons[i] == null) continue;
|
2025-11-17 10:59:59 +01:00
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
|
|
|
|
|
|
Button button = boosterPackButtons[i].GetComponent<Button>();
|
|
|
|
|
|
if (button != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
button.onClick.AddListener(OnBoosterButtonClicked);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-10 14:31:51 +02:00
|
|
|
|
|
2025-11-11 12:32:36 +00:00
|
|
|
|
internal override void OnManagedDestroy()
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-11-17 10:59:59 +01:00
|
|
|
|
// Unsubscribe from book events
|
|
|
|
|
|
if (book != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
book.OnFlip.RemoveListener(OnPageFlipped);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
// Unsubscribe from CardSystemManager
|
|
|
|
|
|
if (CardSystemManager.Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up exit button
|
2025-11-06 01:25:13 +01:00
|
|
|
|
if (exitButton != null)
|
2025-10-15 09:22:13 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
exitButton.onClick.RemoveListener(OnExitButtonClicked);
|
2025-10-15 09:22:13 +02:00
|
|
|
|
}
|
2025-11-06 10:10:54 +01:00
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-07 01:51:03 +01:00
|
|
|
|
|
2025-11-17 10:59:59 +01:00
|
|
|
|
// Clean up pending corner cards
|
|
|
|
|
|
CleanupPendingCornerCards();
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
2025-10-20 13:45:56 +02:00
|
|
|
|
|
2025-11-06 01:25:13 +01:00
|
|
|
|
private void OnExitButtonClicked()
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
if (book != null && book.CurrentPaper != 1)
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
// Not on page 0, flip to page 0 first
|
|
|
|
|
|
BookCurlPro.AutoFlip autoFlip = book.GetComponent<BookCurlPro.AutoFlip>();
|
|
|
|
|
|
if (autoFlip == null)
|
2025-10-15 09:22:13 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
autoFlip = book.gameObject.AddComponent<BookCurlPro.AutoFlip>();
|
2025-10-15 09:22:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 01:25:13 +01:00
|
|
|
|
autoFlip.enabled = true;
|
|
|
|
|
|
autoFlip.StartFlipping(1);
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
2025-11-06 01:25:13 +01:00
|
|
|
|
else
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
// Already on page 0 or no book reference, exit
|
2025-11-06 13:18:39 +01:00
|
|
|
|
// Restore input mode before popping
|
|
|
|
|
|
if (Input.InputManager.Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Input.InputManager.Instance.SetInputMode(_previousInputMode);
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug($"[AlbumViewPage] Restored input mode to {_previousInputMode} on exit");
|
2025-11-06 13:18:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 01:25:13 +01:00
|
|
|
|
if (UIPageController.Instance != null)
|
2025-10-15 09:22:13 +02:00
|
|
|
|
{
|
2025-11-06 01:25:13 +01:00
|
|
|
|
UIPageController.Instance.PopPage();
|
2025-10-15 09:22:13 +02:00
|
|
|
|
}
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
2025-10-15 09:22:13 +02:00
|
|
|
|
}
|
2025-11-06 10:10:54 +01:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-11-06 11:11:15 +01:00
|
|
|
|
// Pass current booster count to the opening page
|
|
|
|
|
|
int boosterCount = CardSystemManager.Instance?.GetBoosterPackCount() ?? 0;
|
|
|
|
|
|
boosterOpeningPage.SetAvailableBoosterCount(boosterCount);
|
|
|
|
|
|
|
2025-11-06 10:10:54 +01:00
|
|
|
|
UIPageController.Instance.PushPage(boosterOpeningPage);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-06 13:18:39 +01:00
|
|
|
|
|
|
|
|
|
|
public override void TransitionIn()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Only store and switch input mode if this is the first time entering
|
2025-11-06 15:27:05 +01:00
|
|
|
|
if (Input.InputManager.Instance != null)
|
2025-11-06 13:18:39 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Store the current input mode before switching
|
|
|
|
|
|
_previousInputMode = Input.InputMode.GameAndUI;
|
|
|
|
|
|
Input.InputManager.Instance.SetInputMode(Input.InputMode.UI);
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Switched to UI-only input mode on first entry");
|
2025-11-06 13:18:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 12:41:28 +01:00
|
|
|
|
// Only spawn pending cards if we're already on an album page (not the menu)
|
|
|
|
|
|
if (IsInAlbumProper())
|
|
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Opening directly to album page - spawning cards immediately");
|
2025-11-17 10:59:59 +01:00
|
|
|
|
SpawnPendingCornerCards();
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Opening to menu page - cards will spawn when entering album");
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
2025-11-07 01:51:03 +01:00
|
|
|
|
|
2025-11-06 13:18:39 +01:00
|
|
|
|
base.TransitionIn();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void TransitionOut()
|
|
|
|
|
|
{
|
2025-11-10 11:55:55 +01:00
|
|
|
|
// Clean up active pending cards to prevent duplicates on next opening
|
2025-11-17 10:59:59 +01:00
|
|
|
|
CleanupPendingCornerCards();
|
2025-11-10 11:55:55 +01:00
|
|
|
|
|
2025-11-06 13:18:39 +01:00
|
|
|
|
// Don't restore input mode here - only restore when actually exiting (in OnExitButtonClicked)
|
|
|
|
|
|
base.TransitionOut();
|
|
|
|
|
|
}
|
2025-11-06 01:25:13 +01:00
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
protected override void DoTransitionIn(System.Action onComplete)
|
|
|
|
|
|
{
|
2025-10-20 13:45:56 +02:00
|
|
|
|
// Simple fade in animation
|
2025-10-15 09:22:13 +02:00
|
|
|
|
if (canvasGroup != null)
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-10-15 09:22:13 +02:00
|
|
|
|
canvasGroup.alpha = 0f;
|
2025-10-27 15:21:23 +01:00
|
|
|
|
Tween.Value(0f, 1f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-10-20 13:45:56 +02:00
|
|
|
|
// Fallback if no CanvasGroup
|
|
|
|
|
|
onComplete?.Invoke();
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-06 01:25:13 +01:00
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
protected override void DoTransitionOut(System.Action onComplete)
|
|
|
|
|
|
{
|
2025-11-07 11:24:19 +01:00
|
|
|
|
// Clean up any enlarged card state before closing
|
|
|
|
|
|
CleanupEnlargedCardState();
|
|
|
|
|
|
|
2025-10-20 13:45:56 +02:00
|
|
|
|
// Simple fade out animation
|
2025-10-15 09:22:13 +02:00
|
|
|
|
if (canvasGroup != null)
|
2025-10-10 14:31:51 +02:00
|
|
|
|
{
|
2025-10-27 15:21:23 +01:00
|
|
|
|
Tween.Value(canvasGroup.alpha, 0f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-10-20 13:45:56 +02:00
|
|
|
|
// Fallback if no CanvasGroup
|
|
|
|
|
|
onComplete?.Invoke();
|
2025-10-15 09:22:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-07 01:51:03 +01:00
|
|
|
|
|
2025-11-07 11:24:19 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Clean up enlarged card state when closing the album
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CleanupEnlargedCardState()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Hide backdrop if visible
|
|
|
|
|
|
if (cardEnlargedBackdrop != null && cardEnlargedBackdrop.activeSelf)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardEnlargedBackdrop.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If there's an enlarged card in the container, return it to its slot
|
|
|
|
|
|
if (cardEnlargedContainer != null && cardEnlargedContainer.childCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = cardEnlargedContainer.childCount - 1; i >= 0; i--)
|
|
|
|
|
|
{
|
|
|
|
|
|
Transform cardTransform = cardEnlargedContainer.GetChild(i);
|
2025-11-16 20:35:54 +01:00
|
|
|
|
var card = cardTransform.GetComponent<StateMachine.Card>();
|
|
|
|
|
|
var state = cardTransform.GetComponentInChildren<StateMachine.States.CardAlbumEnlargedState>(true);
|
|
|
|
|
|
if (card != null && state != null)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
Transform originalParent = state.GetOriginalParent();
|
2025-11-07 11:24:19 +01:00
|
|
|
|
if (originalParent != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardTransform.SetParent(originalParent, true);
|
2025-11-16 20:35:54 +01:00
|
|
|
|
cardTransform.localPosition = state.GetOriginalLocalPosition();
|
|
|
|
|
|
cardTransform.localRotation = state.GetOriginalLocalRotation();
|
2025-11-07 11:24:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 12:41:28 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Check if we're currently viewing the album proper (not the menu page)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsInAlbumProper()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (book == null)
|
|
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Warning("[AlbumViewPage] Book reference is null in IsInAlbumProper check");
|
2025-11-10 12:41:28 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Page 1 is the menu/cover, page 2+ are album pages with card slots
|
|
|
|
|
|
bool inAlbum = book.CurrentPaper > 1;
|
|
|
|
|
|
return inAlbum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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();
|
2025-11-17 10:59:59 +01:00
|
|
|
|
if (isInAlbum && _pendingCornerCards.Count == 0)
|
2025-11-10 12:41:28 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Entering album proper and no cards spawned yet - spawn them with animation
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Entering album proper - spawning pending cards with animation");
|
2025-11-17 10:59:59 +01:00
|
|
|
|
SpawnPendingCornerCards();
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
2025-11-17 10:59:59 +01:00
|
|
|
|
else if (!isInAlbum && _pendingCornerCards.Count > 0)
|
2025-11-10 12:41:28 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Returning to menu page - cleanup cards
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug("[AlbumViewPage] Returning to menu page - cleaning up pending cards");
|
2025-11-17 10:59:59 +01:00
|
|
|
|
CleanupPendingCornerCards();
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-11-10 13:03:36 +01:00
|
|
|
|
Logging.Debug($"[AlbumViewPage] Page flipped but no card state change needed (already in correct state)");
|
2025-11-10 12:41:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
#region Card Enlarge System (Album Slots)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-11-16 20:35:54 +01:00
|
|
|
|
/// 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.
|
2025-11-07 11:24:19 +01:00
|
|
|
|
/// </summary>
|
2025-11-16 20:35:54 +01:00
|
|
|
|
public void RegisterCardInAlbum(StateMachine.Card card)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (card == null) return;
|
|
|
|
|
|
var enlargeState = card.GetStateComponent<StateMachine.States.CardAlbumEnlargedState>("AlbumEnlargedState");
|
|
|
|
|
|
if (enlargeState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
enlargeState.OnEnlargeRequested += OnCardEnlargeRequested;
|
|
|
|
|
|
enlargeState.OnShrinkRequested += OnCardShrinkRequested;
|
|
|
|
|
|
}
|
2025-11-07 11:24:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
public void UnregisterCardInAlbum(StateMachine.Card card)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (card == null) return;
|
|
|
|
|
|
var enlargeState = card.GetStateComponent<StateMachine.States.CardAlbumEnlargedState>("AlbumEnlargedState");
|
|
|
|
|
|
if (enlargeState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
enlargeState.OnEnlargeRequested -= OnCardEnlargeRequested;
|
|
|
|
|
|
enlargeState.OnShrinkRequested -= OnCardShrinkRequested;
|
|
|
|
|
|
}
|
2025-11-07 11:24:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
private void OnCardEnlargeRequested(StateMachine.States.CardAlbumEnlargedState state)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (state == null) return;
|
2025-11-07 11:24:19 +01:00
|
|
|
|
// Show backdrop
|
|
|
|
|
|
if (cardEnlargedBackdrop != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardEnlargedBackdrop.SetActive(true);
|
|
|
|
|
|
}
|
2025-11-16 20:35:54 +01:00
|
|
|
|
// Reparent card root to enlarged container preserving world transform
|
2025-11-07 11:24:19 +01:00
|
|
|
|
if (cardEnlargedContainer != null)
|
|
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
var ctx = state.GetComponentInParent<StateMachine.CardContext>();
|
|
|
|
|
|
if (ctx != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.RootTransform.SetParent(cardEnlargedContainer, true);
|
|
|
|
|
|
ctx.RootTransform.SetAsLastSibling();
|
|
|
|
|
|
}
|
2025-11-07 11:24:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
private void OnCardShrinkRequested(StateMachine.States.CardAlbumEnlargedState state)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (state == null) return;
|
2025-11-07 11:24:19 +01:00
|
|
|
|
// Hide backdrop
|
|
|
|
|
|
if (cardEnlargedBackdrop != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardEnlargedBackdrop.SetActive(false);
|
|
|
|
|
|
}
|
2025-11-16 20:35:54 +01:00
|
|
|
|
// Reparent back to original parent and restore local transform
|
|
|
|
|
|
var ctx = state.GetComponentInParent<StateMachine.CardContext>();
|
|
|
|
|
|
if (ctx != null)
|
2025-11-07 11:24:19 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
Transform originalParent = state.GetOriginalParent();
|
|
|
|
|
|
if (originalParent != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctx.RootTransform.SetParent(originalParent, true);
|
|
|
|
|
|
ctx.RootTransform.localPosition = state.GetOriginalLocalPosition();
|
|
|
|
|
|
ctx.RootTransform.localRotation = state.GetOriginalLocalRotation();
|
|
|
|
|
|
}
|
2025-11-07 11:24:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
#region New Pending Corner Card System
|
2025-11-07 12:51:45 +01:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-11-16 20:35:54 +01:00
|
|
|
|
/// Find a slot by its SlotIndex property
|
2025-11-07 12:51:45 +01:00
|
|
|
|
/// </summary>
|
2025-11-16 20:35:54 +01:00
|
|
|
|
private DraggableSlot FindSlotByIndex(int slotIndex)
|
2025-11-07 12:51:45 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (bottomRightSlots == null)
|
|
|
|
|
|
return null;
|
2025-11-07 12:51:45 +01:00
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
foreach (var slot in bottomRightSlots.Slots)
|
2025-11-07 12:51:45 +01:00
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
if (slot.SlotIndex == slotIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return slot;
|
|
|
|
|
|
}
|
2025-11-07 12:51:45 +01:00
|
|
|
|
}
|
2025-11-16 20:35:54 +01:00
|
|
|
|
return null;
|
2025-11-07 12:51:45 +01:00
|
|
|
|
}
|
2025-11-17 08:39:41 +01:00
|
|
|
|
|
|
|
|
|
|
public void SpawnPendingCornerCards()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cardPrefab == null || bottomRightSlots == null) return;
|
|
|
|
|
|
CleanupPendingCornerCards();
|
2025-11-17 14:30:07 +01:00
|
|
|
|
|
|
|
|
|
|
// Get unique pending cards
|
|
|
|
|
|
var uniquePending = GetUniquePendingCards();
|
|
|
|
|
|
|
|
|
|
|
|
if (uniquePending.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Debug("[AlbumViewPage] No pending cards to spawn");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Spawn min(unique count, MaxPendingCorner)
|
|
|
|
|
|
int spawnCount = Mathf.Min(uniquePending.Count, MaxPendingCorner);
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Spawning {spawnCount} pending corner cards (out of {uniquePending.Count} unique pending)");
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < spawnCount; i++)
|
2025-11-17 08:39:41 +01:00
|
|
|
|
{
|
|
|
|
|
|
var slot = FindSlotByIndex(i);
|
|
|
|
|
|
if (slot == null) break;
|
2025-11-17 14:30:07 +01:00
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
GameObject cardObj = Instantiate(cardPrefab, bottomRightSlots.transform);
|
|
|
|
|
|
var card = cardObj.GetComponent<StateMachine.Card>();
|
|
|
|
|
|
if (card != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
card.SetupForAlbumPending();
|
|
|
|
|
|
card.AssignToSlot(slot, true);
|
2025-11-17 14:30:07 +01:00
|
|
|
|
|
|
|
|
|
|
// Subscribe to both drag events
|
2025-11-17 10:59:59 +01:00
|
|
|
|
card.Context.OnDragStarted += OnCardDragStarted;
|
2025-11-17 14:30:07 +01:00
|
|
|
|
card.Context.OnDragEnded += OnCardDragEnded;
|
|
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
_pendingCornerCards.Add(card);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Destroy(cardObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
/// <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($"[AlbumViewPage] Found {duplicateCount} duplicate pending cards (same definition+rarity combo)");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new List<CardData>(uniqueDict.Values);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
private void CleanupPendingCornerCards()
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var c in _pendingCornerCards)
|
|
|
|
|
|
{
|
2025-11-17 10:59:59 +01:00
|
|
|
|
if (c != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (c.Context != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
c.Context.OnDragStarted -= OnCardDragStarted;
|
2025-11-17 14:30:07 +01:00
|
|
|
|
c.Context.OnDragEnded -= OnCardDragEnded;
|
2025-11-17 10:59:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
Destroy(c.gameObject);
|
|
|
|
|
|
}
|
2025-11-17 08:39:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
_pendingCornerCards.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 10:59:59 +01:00
|
|
|
|
private void OnCardDragStarted(StateMachine.CardContext context)
|
2025-11-17 08:39:41 +01:00
|
|
|
|
{
|
2025-11-17 10:59:59 +01:00
|
|
|
|
if (context == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Only handle if in PendingFaceDownState
|
|
|
|
|
|
var card = context.GetComponent<StateMachine.Card>();
|
|
|
|
|
|
if (card == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
string stateName = card.GetCurrentStateName();
|
2025-11-17 14:30:07 +01:00
|
|
|
|
Logging.Debug($"[AlbumViewPage] OnCardDragStarted - Card state: {stateName}");
|
|
|
|
|
|
|
2025-11-17 10:59:59 +01:00
|
|
|
|
if (stateName != "PendingFaceDownState") return;
|
|
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
// Select smart pending card data
|
|
|
|
|
|
var selected = SelectSmartPendingCard();
|
|
|
|
|
|
if (selected == null)
|
|
|
|
|
|
{
|
2025-11-17 14:30:07 +01:00
|
|
|
|
Logging.Warning("[AlbumViewPage] No pending card data available!");
|
2025-11-17 08:39:41 +01:00
|
|
|
|
return; // no pending data
|
|
|
|
|
|
}
|
2025-11-17 14:30:07 +01:00
|
|
|
|
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Selected card: {selected.Name} ({selected.DefinitionId}), Zone: {selected.Zone}");
|
2025-11-17 10:59:59 +01:00
|
|
|
|
context.SetupCard(selected);
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
// Find target page based on card's zone using BookTabButtons
|
|
|
|
|
|
int targetPage = FindPageForZone(selected.Zone);
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Target page for zone {selected.Zone}: {targetPage}");
|
|
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
if (targetPage >= 0)
|
|
|
|
|
|
{
|
2025-11-17 14:30:07 +01:00
|
|
|
|
// Always flip to the zone's page (even if already there, for consistency)
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Flipping to page {targetPage} for zone {selected.Zone}");
|
|
|
|
|
|
_waitingForPageFlip = true;
|
|
|
|
|
|
_cardDragReleased = false;
|
|
|
|
|
|
_pendingPlacementCard = card;
|
|
|
|
|
|
|
|
|
|
|
|
NavigateToAlbumPage(targetPage, OnPageFlipComplete);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// No valid page found for zone - don't wait for flip
|
|
|
|
|
|
Logging.Warning($"[AlbumViewPage] No BookTabButton found for zone {selected.Zone}");
|
|
|
|
|
|
_waitingForPageFlip = false;
|
|
|
|
|
|
_cardDragReleased = false;
|
|
|
|
|
|
_pendingPlacementCard = card;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnCardDragEnded(StateMachine.CardContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (context == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
var card = context.GetComponent<StateMachine.Card>();
|
|
|
|
|
|
if (card == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Only handle if this is the pending placement card
|
|
|
|
|
|
if (card != _pendingPlacementCard) return;
|
|
|
|
|
|
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Card drag released for: {card.CardData?.Name}");
|
|
|
|
|
|
|
|
|
|
|
|
// Mark drag as released - don't capture slot yet (pages may still be flipping)
|
|
|
|
|
|
_cardDragReleased = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Try to place card (will check if flip is also complete)
|
|
|
|
|
|
TryPlaceCard();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnPageFlipComplete()
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Debug("[AlbumViewPage] Page flip complete");
|
|
|
|
|
|
_waitingForPageFlip = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Try to place card (will check if drag is also released)
|
|
|
|
|
|
TryPlaceCard();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TryPlaceCard()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if BOTH conditions are met:
|
|
|
|
|
|
// 1. Card has been drag-released
|
|
|
|
|
|
// 2. Page flip is complete (or wasn't needed)
|
|
|
|
|
|
if (_cardDragReleased && !_waitingForPageFlip)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_pendingPlacementCard == null || _pendingPlacementCard.CardData == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Warning("[AlbumViewPage] TryPlaceCard - No pending card or card data");
|
|
|
|
|
|
ResetPlacementState();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find the correct slot for this card (AFTER pages have flipped)
|
|
|
|
|
|
var targetSlot = FindTargetSlotForCard(_pendingPlacementCard.CardData);
|
|
|
|
|
|
|
|
|
|
|
|
if (targetSlot == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Warning($"[AlbumViewPage] No slot found for card {_pendingPlacementCard.CardData.DefinitionId}");
|
|
|
|
|
|
// TODO: Return card to corner
|
|
|
|
|
|
ResetPlacementState();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Both conditions met and slot found - perform placement
|
|
|
|
|
|
PlaceCardInSlot(_pendingPlacementCard, targetSlot);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Find the AlbumCardSlot that accepts this card based on DefinitionId
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private AlbumCardSlot FindTargetSlotForCard(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)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Found target slot for {cardData.DefinitionId}, slot: {slot}");
|
|
|
|
|
|
return slot;
|
|
|
|
|
|
}
|
2025-11-17 08:39:41 +01:00
|
|
|
|
}
|
2025-11-17 10:59:59 +01:00
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void PlaceCardInSlot(StateMachine.Card card, AlbumCardSlot slot)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (card == null || slot == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Placing card '{card.CardData?.Name}' in slot - starting tween");
|
|
|
|
|
|
|
|
|
|
|
|
// Reparent to slot immediately, keeping world position (card stays visible where it is)
|
|
|
|
|
|
card.transform.SetParent(slot.transform, true);
|
|
|
|
|
|
|
|
|
|
|
|
// Tween local position and scale simultaneously
|
|
|
|
|
|
float tweenDuration = 0.4f;
|
|
|
|
|
|
|
|
|
|
|
|
// Tween position to center of slot
|
|
|
|
|
|
Tween.LocalPosition(card.transform, Vector3.zero, tweenDuration, 0f, Tween.EaseOutBack);
|
|
|
|
|
|
|
|
|
|
|
|
// Tween scale to normal (same duration and easing)
|
|
|
|
|
|
Tween.LocalScale(card.transform, Vector3.one, tweenDuration, 0f, Tween.EaseOutBack);
|
|
|
|
|
|
|
|
|
|
|
|
// Tween rotation to identity (use this for the completion callback since all tweens are synchronized)
|
|
|
|
|
|
Tween.LocalRotation(card.transform, Quaternion.identity, tweenDuration, 0f, Tween.EaseOutBack,
|
|
|
|
|
|
completeCallback: () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// After tween completes, finalize
|
|
|
|
|
|
card.transform.localPosition = Vector3.zero;
|
|
|
|
|
|
card.transform.localRotation = Quaternion.identity;
|
|
|
|
|
|
|
|
|
|
|
|
// Resize to match slot
|
|
|
|
|
|
RectTransform cardRect = card.transform as RectTransform;
|
|
|
|
|
|
RectTransform slotRect = slot.transform as RectTransform;
|
|
|
|
|
|
if (cardRect != null && slotRect != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
float targetHeight = slotRect.rect.height;
|
|
|
|
|
|
cardRect.sizeDelta = new Vector2(cardRect.sizeDelta.x, targetHeight);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Transition to placed state
|
|
|
|
|
|
var placedState = card.GetStateComponent<StateMachine.States.CardPlacedInSlotState>("PlacedInSlotState");
|
|
|
|
|
|
if (placedState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
placedState.SetParentSlot(slot);
|
|
|
|
|
|
}
|
|
|
|
|
|
card.ChangeState("PlacedInSlotState");
|
|
|
|
|
|
|
|
|
|
|
|
// Assign card to slot (so slot remembers it has a card)
|
|
|
|
|
|
slot.AssignCard(card);
|
|
|
|
|
|
|
|
|
|
|
|
// Mark as placed in inventory
|
|
|
|
|
|
if (CardSystemManager.Instance != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CardSystemManager.Instance.MarkCardAsPlaced(card.CardData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Unsubscribe from drag events - card is now placed, shouldn't respond to future drags
|
|
|
|
|
|
if (card.Context != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
card.Context.OnDragStarted -= OnCardDragStarted;
|
|
|
|
|
|
card.Context.OnDragEnded -= OnCardDragEnded;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Remove from pending corner cards list
|
|
|
|
|
|
_pendingCornerCards.Remove(card);
|
|
|
|
|
|
|
|
|
|
|
|
// Register with album page for enlarge system
|
|
|
|
|
|
RegisterCardInAlbum(card);
|
|
|
|
|
|
|
|
|
|
|
|
Logging.Debug($"[AlbumViewPage] Card placement complete and unsubscribed from drag events");
|
|
|
|
|
|
|
|
|
|
|
|
// Reset state for next card
|
|
|
|
|
|
ResetPlacementState();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ResetPlacementState()
|
|
|
|
|
|
{
|
|
|
|
|
|
_pendingPlacementCard = null;
|
|
|
|
|
|
_waitingForPageFlip = false;
|
|
|
|
|
|
_cardDragReleased = false;
|
2025-11-17 08:39:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private CardData SelectSmartPendingCard()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CardSystemManager.Instance == null) return null;
|
|
|
|
|
|
var pending = CardSystemManager.Instance.GetPendingRevealCards();
|
|
|
|
|
|
if (pending.Count == 0) return null;
|
|
|
|
|
|
// Try current page match
|
|
|
|
|
|
var pageDefs = GetDefinitionsOnCurrentPage();
|
|
|
|
|
|
var match = pending.Find(c => pageDefs.Contains(c.DefinitionId));
|
|
|
|
|
|
if (match != null) return match;
|
|
|
|
|
|
// Fallback random
|
|
|
|
|
|
int idx = Random.Range(0, pending.Count);
|
|
|
|
|
|
return pending[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
/// <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("[AlbumViewPage] No zone tabs discovered!");
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var tab in _zoneTabs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (tab.Zone == zone)
|
|
|
|
|
|
{
|
|
|
|
|
|
return tab.TargetPage;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Logging.Warning($"[AlbumViewPage] No BookTabButton found for zone {zone}");
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 08:39:41 +01:00
|
|
|
|
private List<string> GetDefinitionsOnCurrentPage()
|
|
|
|
|
|
{
|
2025-11-17 10:59:59 +01:00
|
|
|
|
var result = new List<string>();
|
|
|
|
|
|
if (book == null) return result;
|
|
|
|
|
|
|
|
|
|
|
|
int currentPage = book.CurrentPaper;
|
|
|
|
|
|
|
|
|
|
|
|
// Find all AlbumCardSlot in scene
|
|
|
|
|
|
var allSlots = 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-11-17 08:39:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
private void NavigateToAlbumPage(int pageIndex, UnityEngine.Events.UnityAction onComplete = null)
|
2025-11-17 08:39:41 +01:00
|
|
|
|
{
|
2025-11-17 10:59:59 +01:00
|
|
|
|
if (book == null || pageIndex < 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Get or add AutoFlip component
|
|
|
|
|
|
BookCurlPro.AutoFlip autoFlip = book.GetComponent<BookCurlPro.AutoFlip>();
|
|
|
|
|
|
if (autoFlip == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
autoFlip = book.gameObject.AddComponent<BookCurlPro.AutoFlip>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-17 14:30:07 +01:00
|
|
|
|
// Start flipping to target page with callback
|
2025-11-17 10:59:59 +01:00
|
|
|
|
autoFlip.enabled = true;
|
2025-11-17 14:30:07 +01:00
|
|
|
|
if (onComplete != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
autoFlip.StartFlipping(pageIndex, onComplete);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
autoFlip.StartFlipping(pageIndex);
|
|
|
|
|
|
}
|
2025-11-17 08:39:41 +01:00
|
|
|
|
}
|
2025-11-17 14:30:07 +01:00
|
|
|
|
|
|
|
|
|
|
#endregion
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|