Files
YachtDice/Assets/Scripts/Modifiers/Editor/ModifierInventoryDebugWindow.cs
T
2026-03-28 12:53:34 +07:00

311 lines
11 KiB
C#

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<ModifierDefinition> _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<ModifierInventoryDebugWindow>("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<ModifierInstance>(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("<missing definition>", 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)
? "<unnamed>"
: definition.DisplayName;
var id = string.IsNullOrWhiteSpace(definition.Id)
? "<no-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 = FindAnyObjectByType<InventoryController>();
}
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<ModifierDefinition>(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();
}
}
}