2025-10-10 14:31:51 +02:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using AppleHills.Data.CardSystem;
|
2025-10-14 15:53:58 +02:00
|
|
|
|
using Core;
|
2025-10-14 14:57:50 +02:00
|
|
|
|
using Data.CardSystem;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
using Pixelplacement;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
|
|
namespace AppleHills.UI.CardSystem
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// UI page for viewing and organizing the player's card collection in an album.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class AlbumViewPage : UIPage
|
|
|
|
|
|
{
|
|
|
|
|
|
[Header("Album UI Elements")]
|
|
|
|
|
|
[SerializeField] private GridLayoutGroup albumGrid;
|
|
|
|
|
|
[SerializeField] private RectTransform cardStackContainer;
|
|
|
|
|
|
[SerializeField] private GameObject emptyAlbumMessage;
|
|
|
|
|
|
[SerializeField] private GameObject cardSlotPrefab;
|
|
|
|
|
|
[SerializeField] private GameObject cardPrefab;
|
|
|
|
|
|
|
|
|
|
|
|
// Runtime references
|
|
|
|
|
|
private CardSystemManager _cardManager;
|
|
|
|
|
|
private List<CardUIElement> _displayedCards = new List<CardUIElement>();
|
|
|
|
|
|
private Dictionary<int, Transform> _albumSlots = new Dictionary<int, Transform>();
|
|
|
|
|
|
|
|
|
|
|
|
// Drag and drop handling
|
|
|
|
|
|
private CardUIElement _currentlyDraggedCard = null;
|
|
|
|
|
|
private Vector3 _cardOriginalPosition;
|
|
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
_cardManager = CardSystemManager.Instance;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets up the album when the page becomes active
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override void TransitionIn()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.TransitionIn();
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize the album when the page becomes active
|
|
|
|
|
|
InitializeAlbum();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Initializes the album with card slots and the player's collection
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeAlbum()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Clear any previous setup
|
|
|
|
|
|
ClearAlbum();
|
|
|
|
|
|
|
|
|
|
|
|
// Get all collected cards
|
|
|
|
|
|
List<CardData> collectedCards = _cardManager.GetAllCollectedCards();
|
|
|
|
|
|
|
|
|
|
|
|
// Show/hide empty message based on collection
|
|
|
|
|
|
if (emptyAlbumMessage != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
emptyAlbumMessage.SetActive(collectedCards.Count == 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (collectedCards.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set up the album slots
|
|
|
|
|
|
SetupAlbumSlots();
|
|
|
|
|
|
|
|
|
|
|
|
// Create card UI elements for the stack
|
|
|
|
|
|
CreateCardStack(collectedCards);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets up empty slots in the album grid
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SetupAlbumSlots()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (albumGrid == null || cardSlotPrefab == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Create predefined slots in the album
|
|
|
|
|
|
// For a simple implementation, we'll create a 5x5 grid of slots
|
|
|
|
|
|
int slotsPerZone = 5; // 5 slots per zone (one row)
|
|
|
|
|
|
int totalZones = System.Enum.GetValues(typeof(CardZone)).Length;
|
|
|
|
|
|
|
|
|
|
|
|
for (int zone = 0; zone < totalZones; zone++)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < slotsPerZone; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Create a slot at this position
|
|
|
|
|
|
GameObject slotObj = Instantiate(cardSlotPrefab, albumGrid.transform);
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate the collection index for this slot
|
|
|
|
|
|
int collectionIndex = zone * 100 + i; // Zone*100 + position to ensure unique indices
|
|
|
|
|
|
|
|
|
|
|
|
// Store the slot reference
|
|
|
|
|
|
_albumSlots[collectionIndex] = slotObj.transform;
|
|
|
|
|
|
|
|
|
|
|
|
// Set the slot label (optional)
|
|
|
|
|
|
Text slotLabel = slotObj.GetComponentInChildren<Text>();
|
|
|
|
|
|
if (slotLabel != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CardZone zoneEnum = (CardZone)zone;
|
|
|
|
|
|
slotLabel.text = $"{zoneEnum} #{i+1}";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates UI elements for the player's collected cards
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CreateCardStack(List<CardData> cards)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cardStackContainer == null || cardPrefab == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Stack offset for visual effect
|
|
|
|
|
|
Vector3 stackOffset = new Vector3(5f, -5f, 0f);
|
|
|
|
|
|
Vector3 basePosition = Vector3.zero;
|
|
|
|
|
|
|
|
|
|
|
|
// Sort cards by collection index
|
|
|
|
|
|
cards.Sort((a, b) => a.CollectionIndex.CompareTo(b.CollectionIndex));
|
|
|
|
|
|
|
|
|
|
|
|
// Create card UI elements
|
|
|
|
|
|
for (int i = 0; i < cards.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
GameObject cardObj = Instantiate(cardPrefab, cardStackContainer);
|
|
|
|
|
|
CardUIElement cardUI = cardObj.GetComponent<CardUIElement>();
|
|
|
|
|
|
|
|
|
|
|
|
if (cardUI != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Position in stack
|
|
|
|
|
|
cardObj.GetComponent<RectTransform>().anchoredPosition = basePosition + (stackOffset * i);
|
|
|
|
|
|
|
|
|
|
|
|
// Set up card data
|
|
|
|
|
|
cardUI.SetupCard(cards[i]);
|
|
|
|
|
|
|
|
|
|
|
|
// Add drag handlers
|
|
|
|
|
|
SetupCardDragHandlers(cardUI);
|
|
|
|
|
|
|
|
|
|
|
|
// Add to tracked cards
|
|
|
|
|
|
_displayedCards.Add(cardUI);
|
|
|
|
|
|
|
|
|
|
|
|
// Check if this card should be placed in a slot already
|
|
|
|
|
|
int collectionIndex = cards[i].CollectionIndex;
|
|
|
|
|
|
if (_albumSlots.TryGetValue(collectionIndex, out Transform slot))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Card has a designated slot, place it there
|
|
|
|
|
|
PlaceCardInSlot(cardUI, slot);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets up drag and drop handlers for a card
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SetupCardDragHandlers(CardUIElement cardUI)
|
|
|
|
|
|
{
|
|
|
|
|
|
// // Get drag handler component (you might need to implement this)
|
|
|
|
|
|
// DragHandler dragHandler = cardUI.GetComponent<DragHandler>();
|
|
|
|
|
|
// if (dragHandler == null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // This is a stub for the drag handler
|
|
|
|
|
|
// // In a real implementation, you'd have a proper drag handler component
|
|
|
|
|
|
// // For now, we'll just add click listeners
|
|
|
|
|
|
//
|
|
|
|
|
|
// // Add click listener
|
|
|
|
|
|
// Button cardButton = cardUI.GetComponent<Button>();
|
|
|
|
|
|
// if (cardButton != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// cardButton.onClick.AddListener(() => OnCardClicked(cardUI));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// // Set up the drag handler events
|
|
|
|
|
|
// dragHandler.OnBeginDrag.AddListener(() => OnBeginDragCard(cardUI));
|
|
|
|
|
|
// dragHandler.OnDrag.AddListener((position) => OnDragCard(cardUI, position));
|
|
|
|
|
|
// dragHandler.OnEndDrag.AddListener(() => OnEndDragCard(cardUI));
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handles when a card is clicked (simplistic alternative to drag and drop)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnCardClicked(CardUIElement cardUI)
|
|
|
|
|
|
{
|
|
|
|
|
|
CardData cardData = cardUI.GetCardData();
|
|
|
|
|
|
|
|
|
|
|
|
// Find the slot for this card based on collection index
|
|
|
|
|
|
if (_albumSlots.TryGetValue(cardData.CollectionIndex, out Transform slot))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Place the card in its slot
|
|
|
|
|
|
PlaceCardInSlot(cardUI, slot);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handles the start of a card drag operation
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnBeginDragCard(CardUIElement cardUI)
|
|
|
|
|
|
{
|
|
|
|
|
|
_currentlyDraggedCard = cardUI;
|
|
|
|
|
|
_cardOriginalPosition = cardUI.transform.position;
|
|
|
|
|
|
|
|
|
|
|
|
// Bring the card to the front
|
|
|
|
|
|
cardUI.transform.SetAsLastSibling();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handles the dragging of a card
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnDragCard(CardUIElement cardUI, Vector3 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
cardUI.transform.position = position;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Handles the end of a card drag operation
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnEndDragCard(CardUIElement cardUI)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if the card is over a valid slot
|
|
|
|
|
|
Transform closestSlot = FindClosestSlot(cardUI.transform.position);
|
|
|
|
|
|
|
|
|
|
|
|
if (closestSlot != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Place the card in the slot
|
|
|
|
|
|
PlaceCardInSlot(cardUI, closestSlot);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Return the card to its original position
|
|
|
|
|
|
cardUI.transform.position = _cardOriginalPosition;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_currentlyDraggedCard = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Places a card in an album slot
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void PlaceCardInSlot(CardUIElement cardUI, Transform slot)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Reparent the card to the slot
|
|
|
|
|
|
cardUI.transform.SetParent(slot);
|
|
|
|
|
|
|
|
|
|
|
|
// Animate card to center of slot using Pixelplacement.Tween
|
|
|
|
|
|
Tween.LocalPosition(cardUI.transform, Vector3.zero, 0.25f, 0f, Tween.EaseOutBack);
|
|
|
|
|
|
|
2025-10-14 15:53:58 +02:00
|
|
|
|
Logging.Debug($"[AlbumViewPage] Placed card '{cardUI.GetCardData().Name}' in album slot");
|
2025-10-10 14:31:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Finds the closest album slot to a given position
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Transform FindClosestSlot(Vector3 position)
|
|
|
|
|
|
{
|
|
|
|
|
|
Transform closest = null;
|
|
|
|
|
|
float closestDistance = 100f; // Large initial value
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var slot in _albumSlots.Values)
|
|
|
|
|
|
{
|
|
|
|
|
|
float distance = Vector3.Distance(position, slot.position);
|
|
|
|
|
|
if (distance < closestDistance && distance < 50f) // Only if within reasonable range
|
|
|
|
|
|
{
|
|
|
|
|
|
closestDistance = distance;
|
|
|
|
|
|
closest = slot;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return closest;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Clears the album UI
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ClearAlbum()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Clear displayed cards
|
|
|
|
|
|
foreach (var card in _displayedCards)
|
|
|
|
|
|
{
|
|
|
|
|
|
Destroy(card.gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
_displayedCards.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Clear album slots
|
|
|
|
|
|
foreach (Transform child in albumGrid.transform)
|
|
|
|
|
|
{
|
|
|
|
|
|
Destroy(child.gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
_albumSlots.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override for transition in animation using Pixelplacement.Tween
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void DoTransitionIn(System.Action onComplete)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Simple slide in animation
|
|
|
|
|
|
RectTransform rt = GetComponent<RectTransform>();
|
|
|
|
|
|
if (rt != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Store the end position
|
|
|
|
|
|
Vector2 endPosition = rt.anchoredPosition;
|
|
|
|
|
|
|
|
|
|
|
|
// Set initial position (off-screen to the right)
|
|
|
|
|
|
rt.anchoredPosition = new Vector2(Screen.width, endPosition.y);
|
|
|
|
|
|
|
|
|
|
|
|
// Animate to end position
|
|
|
|
|
|
Tween.AnchoredPosition(rt, endPosition, transitionDuration, 0f,
|
|
|
|
|
|
Tween.EaseOutStrong, Tween.LoopType.None, null, onComplete);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
onComplete?.Invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override for transition out animation using Pixelplacement.Tween
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected override void DoTransitionOut(System.Action onComplete)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Simple slide out animation
|
|
|
|
|
|
RectTransform rt = GetComponent<RectTransform>();
|
|
|
|
|
|
if (rt != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Animate to off-screen position (to the left)
|
|
|
|
|
|
Vector2 offScreenPosition = new Vector2(-Screen.width, rt.anchoredPosition.y);
|
|
|
|
|
|
|
|
|
|
|
|
Tween.AnchoredPosition(rt, offScreenPosition, transitionDuration, 0f,
|
|
|
|
|
|
Tween.EaseInOutStrong, Tween.LoopType.None, null, onComplete);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
onComplete?.Invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|