using System.Collections.Generic;
using System.IO;
using System.Linq;
using AppleHills.Data.CardSystem;
using UnityEditor;
using UnityEditor.AddressableAssets.Settings;
using UnityEngine;
using UnityEngine.UI;
using AppleHills.Editor.Utilities;
using UI.CardSystem;
namespace AppleHills.Editor.CardSystem
{
///
/// Editor utility for managing card definitions without directly editing scriptable objects.
/// Provides a searchable list and visual preview of cards.
///
public class CardEditorWindow : EditorWindow
{
// Paths
private const string CardDefinitionsPath = "Assets/Data/Cards";
private const string MenuPath = "AppleHills/Card Editor";
private const string CardUIPrefabPath = "Assets/Prefabs/UI/Cards/SIngleCardDisplayUI.prefab";
private const string CardVisualConfigPath = CardDefinitionsPath + "/CardVisualConfig.asset";
// Editor state
private List _cards = new List();
private CardDefinition _selectedCard;
private CardDefinition _editingCard; // Clone of selected card for editing
private Vector2 _cardListScrollPosition;
private Vector2 _cardEditScrollPosition;
private string _searchQuery = "";
private bool _showPreview = true;
private bool _isDirty = false;
// UI state for card preview
private GameObject _previewCardObject;
private CardUIElement _previewCardElement;
private CardData _previewCardData;
private Rect _previewRect;
private GameObject _cardUIPrefab;
private CardVisualConfig _cardVisualConfig;
private UnityEditor.Editor _cardPreviewEditor;
private Texture2D _staticPreviewTexture;
private bool _previewNeedsUpdate = true;
// Preview settings
private float _previewZoom = 1.0f;
private Vector2 _previewOffset = Vector2.zero;
private bool _debugMode = false;
private float _zoomMultiplier = 1.5f; // Default multiplier (no zoom)
private const float DEFAULT_ZOOM = 1.5f; // Store default zoom as a constant
private const float BASE_ORTHO_SIZE = 400.0f; // Increased from 0.8f to 8.0f for a much wider view
// PreviewRenderUtility for rendering the card in a hidden scene
private PreviewRenderUtility _previewUtility;
private float _orbitRotation = 0f;
private float _autoRotateSpeed = 0f;
private bool _autoRotate = false;
private GameObject _previewInstance;
[MenuItem(MenuPath)]
public static void ShowWindow()
{
var window = GetWindow("Card Editor");
window.minSize = new Vector2(800, 600);
window.Show();
}
private void OnEnable()
{
// Load all card definitions
LoadCardDefinitions();
// Initialize preview
InitializePreview();
// Register for undo/redo
Undo.undoRedoPerformed += OnUndoRedo;
// Set up a repaint on script compilation
EditorApplication.update += OnEditorUpdate;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
private void OnDisable()
{
// Clean up preview
CleanupPreview();
// Unregister from undo/redo
Undo.undoRedoPerformed -= OnUndoRedo;
// Unregister from update
EditorApplication.update -= OnEditorUpdate;
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
}
private void OnEditorUpdate()
{
if (_showPreview && _previewNeedsUpdate)
{
Repaint();
}
// Auto-rotate the preview if enabled
if (_autoRotate && _previewUtility != null)
{
_orbitRotation += _autoRotateSpeed * Time.deltaTime;
Repaint();
}
}
private void OnUndoRedo()
{
// Reload card definitions and refresh
LoadCardDefinitions();
Repaint();
}
private void OnPlayModeStateChanged(PlayModeStateChange stateChange)
{
if (stateChange == PlayModeStateChange.EnteredEditMode)
{
LoadCardDefinitions();
InitializePreview();
}
else if (stateChange == PlayModeStateChange.ExitingEditMode)
{
CleanupPreview();
}
}
private void LoadCardDefinitions()
{
_cards.Clear();
// Create the directory structure if it doesn't exist
string dataDirectory = Path.GetDirectoryName(CardDefinitionsPath); // Gets the "Assets/Data" part
if (!Directory.Exists(dataDirectory))
{
Directory.CreateDirectory(dataDirectory);
}
if (!Directory.Exists(CardDefinitionsPath))
{
Directory.CreateDirectory(CardDefinitionsPath);
AssetDatabase.Refresh();
}
// Find all card definition assets
string[] guids = AssetDatabase.FindAssets("t:CardDefinition", new[] { CardDefinitionsPath });
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
CardDefinition card = AssetDatabase.LoadAssetAtPath(path);
if (card != null)
{
_cards.Add(card);
}
}
// Sort by name
_cards = _cards.OrderBy(c => c.Name).ToList();
// If we had a selected card, try to find it again
if (_selectedCard != null)
{
_selectedCard = _cards.FirstOrDefault(c => c.Id == _selectedCard.Id);
if (_selectedCard != null)
{
_editingCard = CloneCard(_selectedCard);
UpdatePreview();
}
}
}
private void InitializePreview()
{
// Initialize PreviewRenderUtility for 3D preview
if (_previewUtility == null)
{
_previewUtility = new PreviewRenderUtility();
// Configure camera for 2D UI rendering
var cam = _previewUtility.camera;
cam.clearFlags = CameraClearFlags.Color;
cam.backgroundColor = Color.gray; // Background color when nothing is rendered
cam.orthographic = true; // Orthographic is better for 2D UI
cam.orthographicSize = 1f;
cam.nearClipPlane = 0.01f;
cam.farClipPlane = 100f;
cam.cullingMask = ~0; // Render everything
cam.transform.position = new Vector3(0, 0, -6);
cam.transform.rotation = Quaternion.identity;
// Configure lights for better UI visibility
// Key light
_previewUtility.lights[0].intensity = 1.2f;
_previewUtility.lights[0].color = Color.white;
_previewUtility.lights[0].transform.rotation = Quaternion.Euler(30f, 30f, 0f);
// Fill light
_previewUtility.lights[1].intensity = 0.7f;
_previewUtility.lights[1].color = new Color(0.7f, 0.7f, 0.8f);
_previewUtility.lights[1].transform.rotation = Quaternion.Euler(-30f, -30f, 0f);
}
// Load the prefab
_cardUIPrefab = AssetDatabase.LoadAssetAtPath(CardUIPrefabPath);
if (_cardUIPrefab == null)
{
Debug.LogError($"[CardEditorWindow] Could not find card UI prefab at {CardUIPrefabPath}");
return;
}
// Try to load the visual config
_cardVisualConfig = AssetDatabase.LoadAssetAtPath(CardVisualConfigPath);
if (_cardVisualConfig == null)
{
Debug.LogWarning($"[CardEditorWindow] Could not find card visual config at {CardVisualConfigPath}");
}
// Create preview card object
CreatePreviewCardObject();
}
private void CreatePreviewCardObject()
{
// Clean up any existing preview objects
CleanupPreviewInstance();
if (_cardUIPrefab == null)
{
Debug.LogError("[CardEditorWindow] Card UI prefab not loaded");
return;
}
try
{
// Create the preview instance in the PreviewRenderUtility's scene
if (_previewUtility != null)
{
// 1. First create a root GameObject in the preview scene
_previewInstance = new GameObject("PreviewRoot");
_previewInstance.hideFlags = HideFlags.HideAndDontSave;
// 2. Create a Canvas to hold our card UI
GameObject canvasGO = new GameObject("PreviewCanvas");
canvasGO.transform.SetParent(_previewInstance.transform, false);
// 3. Add Canvas component and configure it
Canvas canvas = canvasGO.AddComponent