- **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
335 lines
12 KiB
C#
335 lines
12 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using Data.CardSystem;
|
|
using AppleHills.Data.CardSystem;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace AppleHills.Editor
|
|
{
|
|
/// <summary>
|
|
/// Live preview window for the Card System. Shows real-time collection status.
|
|
/// </summary>
|
|
public class CardSystemLivePreview : EditorWindow
|
|
{
|
|
private Vector2 scrollPosition;
|
|
private bool isSubscribed = false;
|
|
private double lastUpdateTime = 0;
|
|
private const double UPDATE_INTERVAL = 1.0; // Poll every 1 second as backup
|
|
|
|
// Cache for display
|
|
private Dictionary<CardRarity, List<CardData>> cardsByRarity = new Dictionary<CardRarity, List<CardData>>();
|
|
private List<CardData> pendingCards = new List<CardData>();
|
|
private int totalCards = 0;
|
|
private int totalUniqueCards = 0;
|
|
private int totalPendingCards = 0;
|
|
private int boosterCount = 0;
|
|
private string lastEventMessage = "";
|
|
|
|
[MenuItem("AppleHills/Cards/Live Collection Preview")]
|
|
public static void ShowWindow()
|
|
{
|
|
CardSystemLivePreview window = GetWindow<CardSystemLivePreview>("Card Collection Live");
|
|
window.minSize = new Vector2(400, 300);
|
|
window.Show();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
|
|
|
if (Application.isPlaying)
|
|
{
|
|
SubscribeToEvents();
|
|
RefreshData();
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
UnsubscribeFromEvents();
|
|
}
|
|
|
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
|
{
|
|
if (state == PlayModeStateChange.EnteredPlayMode)
|
|
{
|
|
SubscribeToEvents();
|
|
RefreshData();
|
|
}
|
|
else if (state == PlayModeStateChange.ExitingPlayMode)
|
|
{
|
|
UnsubscribeFromEvents();
|
|
}
|
|
}
|
|
|
|
private void SubscribeToEvents()
|
|
{
|
|
if (isSubscribed || !Application.isPlaying) return;
|
|
|
|
if (CardSystemManager.Instance != null)
|
|
{
|
|
CardSystemManager.Instance.OnBoosterOpened += OnBoosterOpened;
|
|
CardSystemManager.Instance.OnCardCollected += OnCardCollected;
|
|
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
|
CardSystemManager.Instance.OnPendingCardAdded += OnPendingCardAdded;
|
|
CardSystemManager.Instance.OnPendingCardRemoved += OnPendingCardRemoved;
|
|
|
|
isSubscribed = true;
|
|
Debug.Log("[CardSystemLivePreview] Subscribed to CardSystemManager events");
|
|
}
|
|
}
|
|
|
|
private void UnsubscribeFromEvents()
|
|
{
|
|
if (!isSubscribed) return;
|
|
|
|
if (CardSystemManager.Instance != null)
|
|
{
|
|
CardSystemManager.Instance.OnBoosterOpened -= OnBoosterOpened;
|
|
CardSystemManager.Instance.OnCardCollected -= OnCardCollected;
|
|
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
|
CardSystemManager.Instance.OnPendingCardAdded -= OnPendingCardAdded;
|
|
CardSystemManager.Instance.OnPendingCardRemoved -= OnPendingCardRemoved;
|
|
}
|
|
|
|
isSubscribed = false;
|
|
}
|
|
|
|
// Event Handlers
|
|
private void OnBoosterOpened(List<CardData> cards)
|
|
{
|
|
lastEventMessage = $"Booster opened! Drew {cards.Count} cards";
|
|
RefreshData();
|
|
Repaint();
|
|
}
|
|
|
|
private void OnCardCollected(CardData card)
|
|
{
|
|
lastEventMessage = $"New card collected: {card.Name} ({card.Rarity})";
|
|
RefreshData();
|
|
Repaint();
|
|
}
|
|
|
|
private void OnBoosterCountChanged(int count)
|
|
{
|
|
boosterCount = count;
|
|
lastEventMessage = $"Booster count changed: {count}";
|
|
Repaint();
|
|
}
|
|
|
|
private void OnPendingCardAdded(CardData card)
|
|
{
|
|
lastEventMessage = $"Pending card added: {card.Name} ({card.Rarity})";
|
|
RefreshData();
|
|
Repaint();
|
|
}
|
|
|
|
private void OnPendingCardRemoved(CardData card)
|
|
{
|
|
lastEventMessage = $"Card removed from pending: {card.Name} ({card.Rarity}) - being placed";
|
|
RefreshData();
|
|
Repaint();
|
|
}
|
|
|
|
private void RefreshData()
|
|
{
|
|
if (!Application.isPlaying || CardSystemManager.Instance == null) return;
|
|
|
|
// Get ONLY collected cards (not pending) to avoid duplicates
|
|
var collectedCards = CardSystemManager.Instance.GetCollectionOnly();
|
|
|
|
// Group by rarity
|
|
cardsByRarity.Clear();
|
|
cardsByRarity[CardRarity.Normal] = collectedCards.Where(c => c.Rarity == CardRarity.Normal).ToList();
|
|
cardsByRarity[CardRarity.Rare] = collectedCards.Where(c => c.Rarity == CardRarity.Rare).ToList();
|
|
cardsByRarity[CardRarity.Legendary] = collectedCards.Where(c => c.Rarity == CardRarity.Legendary).ToList();
|
|
|
|
totalCards = collectedCards.Sum(c => c.CopiesOwned);
|
|
totalUniqueCards = collectedCards.Count;
|
|
boosterCount = CardSystemManager.Instance.GetBoosterPackCount();
|
|
|
|
// Get pending cards separately
|
|
pendingCards = CardSystemManager.Instance.GetPendingRevealCards();
|
|
totalPendingCards = pendingCards.Count;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!Application.isPlaying) return;
|
|
|
|
// Poll every second as backup (in case events are missed)
|
|
if (EditorApplication.timeSinceStartup - lastUpdateTime > UPDATE_INTERVAL)
|
|
{
|
|
lastUpdateTime = EditorApplication.timeSinceStartup;
|
|
|
|
if (!isSubscribed)
|
|
{
|
|
SubscribeToEvents();
|
|
}
|
|
|
|
RefreshData();
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
if (!Application.isPlaying)
|
|
{
|
|
EditorGUILayout.HelpBox("Enter Play Mode to view live collection data.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (CardSystemManager.Instance == null)
|
|
{
|
|
EditorGUILayout.HelpBox("CardSystemManager instance not found!", MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
// Header
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
GUILayout.Label("Live Collection Preview", EditorStyles.boldLabel);
|
|
EditorGUILayout.Space();
|
|
|
|
// Summary Stats
|
|
EditorGUILayout.LabelField("Total Unique Cards:", totalUniqueCards.ToString());
|
|
EditorGUILayout.LabelField("Total Cards Owned:", totalCards.ToString());
|
|
EditorGUILayout.LabelField("Booster Packs:", boosterCount.ToString());
|
|
EditorGUILayout.LabelField("Pending Reveal:", totalPendingCards.ToString(), EditorStyles.boldLabel);
|
|
|
|
if (!string.IsNullOrEmpty(lastEventMessage))
|
|
{
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.HelpBox(lastEventMessage, MessageType.Info);
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
// Collection by Rarity
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
|
|
|
// Pending Cards Section
|
|
if (totalPendingCards > 0)
|
|
{
|
|
DrawPendingCardsSection();
|
|
EditorGUILayout.Space();
|
|
}
|
|
|
|
DrawRaritySection(CardRarity.Legendary, Color.yellow);
|
|
DrawRaritySection(CardRarity.Rare, new Color(0.5f, 0.5f, 1f)); // Light blue
|
|
DrawRaritySection(CardRarity.Normal, Color.white);
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
// Actions
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Refresh Now"))
|
|
{
|
|
RefreshData();
|
|
}
|
|
if (GUILayout.Button("Clear Event Log"))
|
|
{
|
|
lastEventMessage = "";
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawPendingCardsSection()
|
|
{
|
|
var oldColor = GUI.backgroundColor;
|
|
GUI.backgroundColor = new Color(1f, 0.8f, 0.4f); // Orange tint for pending
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
GUI.backgroundColor = oldColor;
|
|
|
|
GUILayout.Label($"⏳ Pending Reveal ({pendingCards.Count} cards)", EditorStyles.boldLabel);
|
|
|
|
// Group pending by rarity for display
|
|
var pendingByRarity = pendingCards.GroupBy(c => c.Rarity).OrderByDescending(g => g.Key);
|
|
|
|
foreach (var rarityGroup in pendingByRarity)
|
|
{
|
|
EditorGUILayout.Space(5);
|
|
var rarityColor = GetRarityColor(rarityGroup.Key);
|
|
|
|
oldColor = GUI.color;
|
|
GUI.color = rarityColor;
|
|
GUILayout.Label($"{rarityGroup.Key} ({rarityGroup.Count()})", EditorStyles.miniBoldLabel);
|
|
GUI.color = oldColor;
|
|
|
|
foreach (var card in rarityGroup.OrderBy(c => c.Name))
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField(" • " + card.Name, GUILayout.Width(200));
|
|
EditorGUILayout.LabelField(card.Zone.ToString(), GUILayout.Width(100));
|
|
EditorGUILayout.LabelField($"Copies: {card.CopiesOwned}", GUILayout.Width(80));
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private Color GetRarityColor(CardRarity rarity)
|
|
{
|
|
switch (rarity)
|
|
{
|
|
case CardRarity.Legendary: return Color.yellow;
|
|
case CardRarity.Rare: return new Color(0.5f, 0.5f, 1f);
|
|
case CardRarity.Normal: return Color.white;
|
|
default: return Color.gray;
|
|
}
|
|
}
|
|
|
|
private void DrawRaritySection(CardRarity rarity, Color color)
|
|
{
|
|
if (!cardsByRarity.ContainsKey(rarity) || cardsByRarity[rarity].Count == 0)
|
|
return;
|
|
|
|
var cards = cardsByRarity[rarity];
|
|
int totalCopies = cards.Sum(c => c.CopiesOwned);
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
// Header
|
|
var oldColor = GUI.backgroundColor;
|
|
GUI.backgroundColor = color;
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
GUI.backgroundColor = oldColor;
|
|
|
|
GUILayout.Label($"{rarity} ({cards.Count} unique, {totalCopies} total)", EditorStyles.boldLabel);
|
|
|
|
// Cards
|
|
foreach (var card in cards.OrderBy(c => c.Name))
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
EditorGUILayout.LabelField(card.Name, GUILayout.Width(200));
|
|
EditorGUILayout.LabelField($"x{card.CopiesOwned}", GUILayout.Width(50));
|
|
EditorGUILayout.LabelField(card.Zone.ToString(), GUILayout.Width(100));
|
|
|
|
// Progress to next tier (if not Legendary)
|
|
if (rarity < CardRarity.Legendary)
|
|
{
|
|
int copiesNeeded = 5;
|
|
float progress = Mathf.Clamp01((float)card.CopiesOwned / copiesNeeded);
|
|
Rect progressRect = GUILayoutUtility.GetRect(100, 18);
|
|
EditorGUI.ProgressBar(progressRect, progress, $"{card.CopiesOwned}/{copiesNeeded}");
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|