Files
YachtDice/Assets/Scripts/Modifiers/Editor/ModifierDefinitionValidator.cs
T
2026-03-02 12:49:12 +07:00

225 lines
8.8 KiB
C#

#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using YachtDice.Modifiers.Definition;
namespace YachtDice.Modifiers.Editor
{
public static class ModifierDefinitionValidator
{
[MenuItem("YachtDice/Validate All Modifier Definitions")]
public static void ValidateAll()
{
string[] guids = AssetDatabase.FindAssets("t:ModifierDefinitionSO");
if (guids.Length == 0)
{
Debug.Log("[ModifierValidator] No ModifierDefinitionSO assets found.");
return;
}
var errorCount = 0;
var warnCount = 0;
var usedIds = new Dictionary<string, string>(); // id -> asset path
foreach (var t in guids)
{
var path = AssetDatabase.GUIDToAssetPath(t);
var def = AssetDatabase.LoadAssetAtPath<ModifierDefinition>(path);
if (def == null)
{
Debug.LogError($"[ModifierValidator] Failed to load asset at {path}");
errorCount++;
continue;
}
// ── Id checks ────────────────────────────────────────
if (string.IsNullOrWhiteSpace(def.Id))
{
Debug.LogError($"[ModifierValidator] {path}: Id is empty.", def);
errorCount++;
}
else if (usedIds.TryGetValue(def.Id, out string existingPath))
{
Debug.LogError($"[ModifierValidator] {path}: Duplicate Id '{def.Id}' (also used by {existingPath}).", def);
errorCount++;
}
else
{
usedIds[def.Id] = path;
}
// ── Economy checks ───────────────────────────────────
if (def.ShopPrice < 0)
{
Debug.LogError($"[ModifierValidator] {path}: ShopPrice is negative ({def.ShopPrice}).", def);
errorCount++;
}
if (def.SellPrice < 0)
{
Debug.LogError($"[ModifierValidator] {path}: SellPrice is negative ({def.SellPrice}).", def);
errorCount++;
}
if (def.SellPrice > def.ShopPrice && def.ShopPrice > 0)
{
Debug.LogWarning($"[ModifierValidator] {path}: SellPrice ({def.SellPrice}) > ShopPrice ({def.ShopPrice}). Infinite money exploit?", def);
warnCount++;
}
// ── Durability checks ────────────────────────────────
if (def.HasLimitedUses && def.MaxUses <= 0)
{
Debug.LogError($"[ModifierValidator] {path}: HasLimitedUses is true but MaxUses is {def.MaxUses}.", def);
errorCount++;
}
if (!def.HasLimitedUses && def.MaxUses > 0)
{
Debug.LogWarning($"[ModifierValidator] {path}: HasLimitedUses is false but MaxUses is {def.MaxUses}. Ignored at runtime.", def);
warnCount++;
}
if (def.MaxStacks < 1)
{
Debug.LogError($"[ModifierValidator] {path}: MaxStacks must be >= 1 (is {def.MaxStacks}).", def);
errorCount++;
}
// ── Behavior checks ──────────────────────────────────
if (def.Behaviors == null || def.Behaviors.Count == 0)
{
Debug.LogWarning($"[ModifierValidator] {path}: No behaviors assigned. Modifier will do nothing.", def);
warnCount++;
continue;
}
for (int b = 0; b < def.Behaviors.Count; b++)
{
var behavior = def.Behaviors[b];
if (behavior == null)
{
Debug.LogError($"[ModifierValidator] {path}: Behavior slot [{b}] is null.", def);
errorCount++;
continue;
}
// Check for null conditions
if (behavior.Conditions != null)
{
for (int c = 0; c < behavior.Conditions.Count; c++)
{
if (behavior.Conditions[c] == null)
{
Debug.LogWarning($"[ModifierValidator] {path}: Behavior '{behavior.name}' has null condition at slot [{c}].", behavior);
warnCount++;
}
}
}
// Check for null or empty effects
if (behavior.Effects == null || behavior.Effects.Count == 0)
{
Debug.LogWarning($"[ModifierValidator] {path}: Behavior '{behavior.name}' has no effects.", behavior);
warnCount++;
}
else
{
for (int e = 0; e < behavior.Effects.Count; e++)
{
if (behavior.Effects[e] == null)
{
Debug.LogError($"[ModifierValidator] {path}: Behavior '{behavior.name}' has null effect at slot [{e}].", behavior);
errorCount++;
}
}
}
}
// ── Display checks ───────────────────────────────────
if (string.IsNullOrWhiteSpace(def.DisplayName))
{
Debug.LogWarning($"[ModifierValidator] {path}: DisplayName is empty.", def);
warnCount++;
}
if (string.IsNullOrWhiteSpace(def.Description))
{
Debug.LogWarning($"[ModifierValidator] {path}: Description is empty.", def);
warnCount++;
}
}
string summary = $"[ModifierValidator] Validated {guids.Length} modifier(s): {errorCount} error(s), {warnCount} warning(s).";
if (errorCount > 0)
Debug.LogError(summary);
else if (warnCount > 0)
Debug.LogWarning(summary);
else
Debug.Log(summary);
}
[MenuItem("YachtDice/Validate Modifier Catalog")]
public static void ValidateCatalog()
{
string[] guids = AssetDatabase.FindAssets("t:ModifierCatalogSO");
if (guids.Length == 0)
{
Debug.LogWarning("[ModifierValidator] No ModifierCatalogSO asset found. Create one via Create > YachtDice/Modifiers/Catalog.");
return;
}
for (int i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
var catalog = AssetDatabase.LoadAssetAtPath<ModifierCatalog>(path);
if (catalog == null)
{
Debug.LogError($"[ModifierValidator] Failed to load catalog at {path}");
continue;
}
if (catalog.All == null || catalog.All.Count == 0)
{
Debug.LogWarning($"[ModifierValidator] Catalog at {path} is empty.");
continue;
}
int nullCount = 0;
var catalogIds = new HashSet<string>();
for (int j = 0; j < catalog.All.Count; j++)
{
if (catalog.All[j] == null)
{
nullCount++;
Debug.LogError($"[ModifierValidator] Catalog {path}: Null entry at index [{j}].", catalog);
continue;
}
string id = catalog.All[j].Id;
if (!catalogIds.Add(id))
{
Debug.LogError($"[ModifierValidator] Catalog {path}: Duplicate modifier Id '{id}' in catalog.", catalog);
}
}
Debug.Log($"[ModifierValidator] Catalog {path}: {catalog.All.Count} entries, {nullCount} null.");
}
}
}
}
#endif