[Add] Debugger Inventory & small ui fixes
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
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 = FindObjectOfType<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user