using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using YachtDice.Inventory; using YachtDice.Modifiers.Definition; using YachtDice.Modifiers.Runtime; namespace YachtDice.Modifiers.Editor { public class ModifierInventoryDebugWindow : EditorWindow { private const double RepaintIntervalSeconds = 0.5d; private readonly List _allDefinitions = new(); private Vector2 _catalogScroll; private Vector2 _inventoryScroll; private string _search = string.Empty; private InventoryController _inventoryController; private double _nextRepaintTime; [MenuItem("YachtDice/Debug/Modifier Inventory")] public static void Open() { var window = GetWindow("Modifier Debug"); window.minSize = new Vector2(700f, 400f); window.RefreshDefinitions(); window.ResolveInventoryController(); } private void OnEnable() { RefreshDefinitions(); ResolveInventoryController(); EditorApplication.playModeStateChanged += HandlePlayModeStateChanged; } private void OnDisable() { EditorApplication.playModeStateChanged -= HandlePlayModeStateChanged; } private void OnFocus() { RefreshDefinitions(); ResolveInventoryController(); } private void OnHierarchyChange() { if (!Application.isPlaying) return; ResolveInventoryController(); Repaint(); } private void Update() { if (EditorApplication.timeSinceStartup < _nextRepaintTime) return; _nextRepaintTime = EditorApplication.timeSinceStartup + RepaintIntervalSeconds; if (Application.isPlaying && (_inventoryController == null || _inventoryController.Model == null)) ResolveInventoryController(); Repaint(); } private void OnGUI() { DrawToolbar(); if (!Application.isPlaying) { EditorGUILayout.HelpBox("Enter Play Mode to edit runtime inventory state.", MessageType.Info); } var model = GetInventoryModel(); if (Application.isPlaying && model == null) { EditorGUILayout.HelpBox("InventoryController was not found in the scene.", MessageType.Warning); } DrawOwnedModifiers(model); EditorGUILayout.Space(8f); DrawDefinitionCatalog(model); } private void DrawToolbar() { using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar)) { if (GUILayout.Button("Refresh Catalog", EditorStyles.toolbarButton)) RefreshDefinitions(); if (GUILayout.Button("Find Inventory", EditorStyles.toolbarButton)) ResolveInventoryController(); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField("Search", GUILayout.Width(45f)); _search = EditorGUILayout.TextField(_search, GUILayout.Width(220f)); } } private void DrawOwnedModifiers(InventoryModel model) { EditorGUILayout.LabelField("Owned Modifiers", EditorStyles.boldLabel); if (model == null) { EditorGUILayout.HelpBox("Inventory is unavailable.", MessageType.None); return; } EditorGUILayout.LabelField($"Active slots: {model.ActiveCount}/{model.MaxActiveSlots}"); var snapshot = new List(model.OwnedModifiers); if (snapshot.Count == 0) { EditorGUILayout.HelpBox("Inventory is empty.", MessageType.None); return; } _inventoryScroll = EditorGUILayout.BeginScrollView(_inventoryScroll, GUILayout.MinHeight(140f)); foreach (var instance in snapshot) { DrawOwnedModifierRow(model, instance); } EditorGUILayout.EndScrollView(); } private void DrawOwnedModifierRow(InventoryModel model, ModifierInstance instance) { var definition = instance.Definition; if (definition == null) { using (new EditorGUILayout.VerticalScope("box")) { EditorGUILayout.LabelField("", EditorStyles.boldLabel); if (GUILayout.Button("Remove", GUILayout.Width(90f))) model.RemoveModifier(instance); } return; } var state = instance.IsActive ? "Active" : "Inactive"; var uses = definition.HasLimitedUses ? $"Uses: {instance.RemainingUses}/{definition.MaxUses}" : "Uses: infinite"; using (new EditorGUILayout.VerticalScope("box")) { EditorGUILayout.LabelField(BuildDefinitionLabel(definition), EditorStyles.boldLabel); EditorGUILayout.LabelField($"State: {state} | {uses}"); using (new EditorGUILayout.HorizontalScope()) { using (new EditorGUI.DisabledScope(instance.IsActive)) { if (GUILayout.Button("Activate", GUILayout.Width(90f))) { if (!model.TryActivate(instance)) Debug.LogWarning($"[ModifierDebug] Failed to activate '{definition.Id}'. Check slot limits."); } } using (new EditorGUI.DisabledScope(!instance.IsActive)) { if (GUILayout.Button("Deactivate", GUILayout.Width(90f))) model.Deactivate(instance); } if (GUILayout.Button("Remove", GUILayout.Width(90f))) model.RemoveModifier(instance); } } } private void DrawDefinitionCatalog(InventoryModel model) { EditorGUILayout.LabelField("All Modifier Definitions", EditorStyles.boldLabel); EditorGUILayout.LabelField($"Loaded assets: {_allDefinitions.Count}"); if (_allDefinitions.Count == 0) { EditorGUILayout.HelpBox("No ModifierDefinition assets found.", MessageType.Warning); return; } _catalogScroll = EditorGUILayout.BeginScrollView(_catalogScroll); var loweredSearch = string.IsNullOrWhiteSpace(_search) ? string.Empty : _search.Trim().ToLowerInvariant(); for (var i = 0; i < _allDefinitions.Count; i++) { var definition = _allDefinitions[i]; if (definition == null) continue; if (!MatchesSearch(definition, loweredSearch)) continue; DrawDefinitionRow(model, definition); } EditorGUILayout.EndScrollView(); } private void DrawDefinitionRow(InventoryModel model, ModifierDefinition definition) { var uses = definition.HasLimitedUses ? $"limited ({definition.MaxUses})" : "permanent"; using (new EditorGUILayout.HorizontalScope("box")) { EditorGUILayout.LabelField($"{BuildDefinitionLabel(definition)} | {uses}"); using (new EditorGUI.DisabledScope(model == null)) { if (GUILayout.Button("Add", GUILayout.Width(90f))) model.AddModifier(definition); } } } private static bool MatchesSearch(ModifierDefinition definition, string loweredSearch) { if (string.IsNullOrEmpty(loweredSearch)) return true; var id = definition.Id ?? string.Empty; var displayName = definition.DisplayName ?? string.Empty; return id.IndexOf(loweredSearch, StringComparison.OrdinalIgnoreCase) >= 0 || displayName.IndexOf(loweredSearch, StringComparison.OrdinalIgnoreCase) >= 0; } private static string BuildDefinitionLabel(ModifierDefinition definition) { var displayName = string.IsNullOrWhiteSpace(definition.DisplayName) ? "" : definition.DisplayName; var id = string.IsNullOrWhiteSpace(definition.Id) ? "" : definition.Id; return $"{displayName} [{id}]"; } private InventoryModel GetInventoryModel() { if (!Application.isPlaying) return null; if (_inventoryController == null) ResolveInventoryController(); return _inventoryController != null ? _inventoryController.Model : null; } private void ResolveInventoryController() { if (!Application.isPlaying) { _inventoryController = null; return; } _inventoryController = FindObjectOfType(); } private void RefreshDefinitions() { _allDefinitions.Clear(); var guids = AssetDatabase.FindAssets("t:ModifierDefinition"); for (var i = 0; i < guids.Length; i++) { var path = AssetDatabase.GUIDToAssetPath(guids[i]); var definition = AssetDatabase.LoadAssetAtPath(path); if (definition != null) _allDefinitions.Add(definition); } _allDefinitions.Sort((a, b) => { var nameComparison = string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase); if (nameComparison != 0) return nameComparison; return string.Compare(a.Id, b.Id, StringComparison.OrdinalIgnoreCase); }); } private void HandlePlayModeStateChanged(PlayModeStateChange change) { if (change == PlayModeStateChange.EnteredPlayMode) ResolveInventoryController(); if (change == PlayModeStateChange.ExitingPlayMode || change == PlayModeStateChange.EnteredEditMode) _inventoryController = null; Repaint(); } } }