using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Logic.Editor { public class VisualSpriteVariantMapEditorWindow : EditorWindow { private const string DefaultMapAssetPath = "Assets/BundleResources/DataAssets/VisualSpriteVariantMap.asset"; private const string GridAndResourceAssetPath = "Assets/BundleResources/Export/GridAndResourceDataAssets.asset"; private const string EyeComfortGroundFolder = "Assets/BundleResources/ArtResources/TH1Grounds/EyeComfort"; private const string EyeComfortMountainFolder = "Assets/BundleResources/ArtResources/TH1Mountains/EyeComfort"; private const string EyeComfortForestFolder = "Assets/BundleResources/ArtResources/TH1Forests/EyeComfort"; private const string GroundGroup = "GridObject/Ground"; private const string MountainGroup = "GridObject/Mountain"; private const string ForestGroup = "GridObject/Forest"; private VisualSpriteVariantMapAsset _mapAsset; private GridAndResourceDataAssets _gridAsset; private Vector2 _scroll; [MenuItem("Tools/TH1/Visual Sprite Variant Map")] private static void OpenWindow() { var window = GetWindow(); window.titleContent = new GUIContent("Sprite Variants"); window.minSize = new Vector2(720, 420); window.LoadAssets(); } [MenuItem("Tools/TH1/Visual Sprite Variant Map/Rebuild Map Feature EyeComfort")] private static void RebuildGroundFromMenu() { var window = CreateInstance(); window.LoadAssets(); window.RebuildMapFeatureMappings(); DestroyImmediate(window); } private void OnEnable() { LoadAssets(); } private void OnGUI() { EditorGUILayout.LabelField("Visual Sprite Variant Map", EditorStyles.boldLabel); using (new EditorGUILayout.HorizontalScope()) { _mapAsset = (VisualSpriteVariantMapAsset)EditorGUILayout.ObjectField("Map Asset", _mapAsset, typeof(VisualSpriteVariantMapAsset), false); if (GUILayout.Button("Create/Load", GUILayout.Width(110))) LoadOrCreateMapAsset(); } _gridAsset = (GridAndResourceDataAssets)EditorGUILayout.ObjectField("Grid Asset", _gridAsset, typeof(GridAndResourceDataAssets), false); using (new EditorGUILayout.HorizontalScope()) { if (GUILayout.Button("Rebuild Map Feature EyeComfort Mappings", GUILayout.Height(28))) RebuildMapFeatureMappings(); if (GUILayout.Button("Select Asset", GUILayout.Width(110), GUILayout.Height(28)) && _mapAsset != null) { Selection.activeObject = _mapAsset; EditorGUIUtility.PingObject(_mapAsset); } } EditorGUILayout.Space(8); if (_mapAsset == null) { EditorGUILayout.HelpBox("Create or load VisualSpriteVariantMap.asset first.", MessageType.Info); return; } int total = _mapAsset.Pairs?.Count ?? 0; int mapped = _mapAsset.CountMappedPairs(VisualTheme.EyeComfort); EditorGUILayout.LabelField($"Pairs: {total} EyeComfort mapped: {mapped}"); var serializedObject = new SerializedObject(_mapAsset); var pairsProperty = serializedObject.FindProperty("Pairs"); _scroll = EditorGUILayout.BeginScrollView(_scroll); EditorGUILayout.PropertyField(pairsProperty, true); EditorGUILayout.EndScrollView(); serializedObject.ApplyModifiedProperties(); } private void LoadAssets() { _mapAsset = AssetDatabase.LoadAssetAtPath(DefaultMapAssetPath); _gridAsset = AssetDatabase.LoadAssetAtPath(GridAndResourceAssetPath); } private void LoadOrCreateMapAsset() { _mapAsset = LoadOrCreateMapAsset(DefaultMapAssetPath); } private static VisualSpriteVariantMapAsset LoadOrCreateMapAsset(string path) { var asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) return asset; var folder = System.IO.Path.GetDirectoryName(path)?.Replace("\\", "/"); if (!string.IsNullOrEmpty(folder) && !AssetDatabase.IsValidFolder(folder)) CreateFolders(folder); asset = CreateInstance(); AssetDatabase.CreateAsset(asset, path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return asset; } private void RebuildMapFeatureMappings() { _mapAsset = LoadOrCreateMapAsset(DefaultMapAssetPath); if (_gridAsset == null) _gridAsset = AssetDatabase.LoadAssetAtPath(GridAndResourceAssetPath); if (_gridAsset == null) { Debug.LogError($"[VisualSpriteVariantMap] Grid asset not found: {GridAndResourceAssetPath}"); return; } Undo.RecordObject(_mapAsset, "Rebuild Map Feature EyeComfort Mappings"); _mapAsset.Pairs.RemoveAll(pair => pair != null && (pair.Group == GroundGroup || pair.Group == MountainGroup || pair.Group == ForestGroup)); int groundMapped = AddMappings(GroundGroup, CollectGroundSprites(_gridAsset), EyeComfortGroundFolder); int mountainMapped = AddMappings(MountainGroup, CollectMountainSprites(_gridAsset), EyeComfortMountainFolder); int forestMapped = AddMappings(ForestGroup, CollectForestSprites(_gridAsset), EyeComfortForestFolder); _mapAsset.RebuildCache(); EditorUtility.SetDirty(_mapAsset); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"[VisualSpriteVariantMap] Rebuilt map feature pairs. Ground={groundMapped}, Mountain={mountainMapped}, Forest={forestMapped} matched EyeComfort sprites."); } private int AddMappings(string group, List sourceSprites, string replacementFolder) { var replacementByName = LoadSpritesByName(replacementFolder); int mapped = 0; foreach (var source in sourceSprites) { replacementByName.TryGetValue(source.name, out var replacement); if (replacement != null) mapped++; _mapAsset.Pairs.Add(new VisualSpriteVariantPair { Group = group, Source = source, EyeComfort = replacement }); } return mapped; } private static List CollectGroundSprites(GridAndResourceDataAssets gridAsset) { var result = new List(); var seen = new HashSet(); if (gridAsset?.TerrainInfoList == null) return result; foreach (var terrain in gridAsset.TerrainInfoList) { if (terrain == null || terrain.TerrainType != TerrainType.Land) continue; AddSprite(result, seen, terrain.Sprite); if (terrain.SpriteList == null) continue; foreach (var spriteInfo in terrain.SpriteList) AddSprite(result, seen, spriteInfo?.Sprite); } return result; } private static List CollectMountainSprites(GridAndResourceDataAssets gridAsset) { var result = new List(); var seen = new HashSet(); if (gridAsset?.MountainInfo?.SpriteList == null) return result; foreach (var spriteInfo in gridAsset.MountainInfo.SpriteList) AddSprite(result, seen, spriteInfo?.Sprite); return result; } private static List CollectForestSprites(GridAndResourceDataAssets gridAsset) { var result = new List(); var seen = new HashSet(); if (gridAsset?.VegetationInfo?.SpriteList == null) return result; foreach (var spriteInfo in gridAsset.VegetationInfo.SpriteList) AddSprite(result, seen, spriteInfo?.Sprite); return result; } private static void AddSprite(List result, HashSet seen, Sprite sprite) { if (sprite == null || seen.Contains(sprite)) return; seen.Add(sprite); result.Add(sprite); } private static Dictionary LoadSpritesByName(string folder) { var result = new Dictionary(); var guids = AssetDatabase.FindAssets("t:Sprite", new[] { folder }); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); var sprites = AssetDatabase.LoadAllAssetsAtPath(path); foreach (var asset in sprites) { if (asset is Sprite sprite && !result.ContainsKey(sprite.name)) result[sprite.name] = sprite; } } return result; } private static void CreateFolders(string folder) { var parts = folder.Split('/'); var current = parts[0]; for (int i = 1; i < parts.Length; i++) { var next = $"{current}/{parts[i]}"; if (!AssetDatabase.IsValidFolder(next)) AssetDatabase.CreateFolder(current, parts[i]); current = next; } } } }