Files
AppleHillsProduction/Assets/Scripts/CardSystem/Controllers/AlbumNavigationService.cs
tschesky 235fa04eba Merge a card refresh (#59)
- **Refactored Card Placement Flow**
  - Separated card presentation from orchestration logic
  - Extracted `CornerCardManager` for pending card lifecycle (spawn, shuffle, rebuild)
  - Extracted `AlbumNavigationService` for book page navigation and zone mapping
  - Extracted `CardEnlargeController` for backdrop management and card reparenting
  - Implemented controller pattern (non-MonoBehaviour) for complex logic
  - Cards now unparent from slots before rebuild to prevent premature destruction

- **Improved Corner Card Display**
  - Fixed cards spawning on top of each other during rebuild
  - Implemented shuffle-to-front logic (remaining cards occupy slots 0→1→2)
  - Added smart card selection (prioritizes cards matching current album page)
  - Pending cards now removed from queue immediately on drag start
  - Corner cards rebuild after each placement with proper slot reassignment

- **Enhanced Album Card Viewing**
  - Added dramatic scale increase when viewing cards from album slots
  - Implemented shrink animation when dismissing enlarged cards
  - Cards transition: `PlacedInSlotState` → `AlbumEnlargedState` → `PlacedInSlotState`
  - Backdrop shows/hides with card enlarge/shrink cycle
  - Cards reparent to enlarged container while viewing, return to slot after

- **State Machine Improvements**
  - Added `CardStateNames` constants for type-safe state transitions
  - Implemented `ICardClickHandler` and `ICardStateDragHandler` interfaces
  - State transitions now use cached property indices
  - `BoosterCardContext` separated from `CardContext` for single responsibility

- **Code Cleanup**
  - Identified unused `SlotContainerHelper.cs` (superseded by `CornerCardManager`)
  - Identified unused `BoosterPackDraggable.canOpenOnDrop` field
  - Identified unused `AlbumViewPage._previousInputMode` (hardcoded value)
  - Identified unused `Card.OnPlacedInAlbumSlot` event (no subscribers)

Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Co-authored-by: Michal Pikulski <michal@foolhardyhorizons.com>
Reviewed-on: #59
2025-11-18 08:40:59 +00:00

173 lines
5.7 KiB
C#

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
}
}