[Add] Asset Validator

This commit is contained in:
2026-04-08 13:18:26 +07:00
parent 211c975889
commit efce7c458d
5 changed files with 211 additions and 13 deletions
@@ -106,6 +106,111 @@ namespace InfiniteWorld.VoxelWorld
public bool HasPrefab { get; }
}
internal static class WorldPlacementValidation
{
public static bool TryGetCollectionBlockerReason(WorldPrefabCollection collection, out string reason)
{
if (collection == null)
{
reason = "collection reference is missing.";
return true;
}
if (collection.maxPlacementsPerChunk <= 0)
{
reason = "max placements per chunk must be greater than 0.";
return true;
}
if (collection.attemptsPerPlacement < 1)
{
reason = "attempts per placement must be at least 1.";
return true;
}
if (collection.chunkEdgePadding < 0)
{
reason = "chunk edge padding cannot be negative.";
return true;
}
if (collection.entries == null || collection.entries.Count == 0)
{
reason = "collection has no entries configured.";
return true;
}
reason = null;
return false;
}
public static bool TryGetEntryBlockerReason(WorldPrefabEntry entry, HashSet<string> usedIds, out string reason)
{
if (entry == null)
{
reason = "entry reference is missing.";
return true;
}
if (string.IsNullOrWhiteSpace(entry.id))
{
reason = "id is empty. A stable id is required for deterministic spawns and save data.";
return true;
}
if (usedIds != null && !usedIds.Add(entry.id))
{
reason = $"duplicate id '{entry.id}' in the same collection.";
return true;
}
if (entry.prefab == null)
{
reason = "prefab is missing.";
return true;
}
if (entry.weight <= 0f)
{
reason = "weight must be greater than 0.";
return true;
}
if (entry.spawnChancePercent <= 0f)
{
reason = "spawn chance must be greater than 0%.";
return true;
}
if (entry.footprint.x <= 0 || entry.footprint.y <= 0)
{
reason = "footprint must be at least 1x1.";
return true;
}
if (entry.clearance < 0)
{
reason = "clearance cannot be negative.";
return true;
}
if (entry.flattenPadding < 0)
{
reason = "flatten padding cannot be negative.";
return true;
}
if (entry.placementMode == WorldPlacementMode.FlattenTerrain && entry.flattenSearchRadius < 1)
{
reason = "flatten search radius must be at least 1 for FlattenTerrain mode.";
return true;
}
reason = null;
return false;
}
}
public sealed class WorldTerrainPatch
{
private readonly List<Vector2Int> flattenedCells;