This commit is contained in:
2026-07-09 21:33:33 +02:00
commit 84a2a365f3
2364 changed files with 950134 additions and 0 deletions
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c9ced093873747b6a67a3c6a11fdda99
timeCreated: 1708460670
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d8cd96d9d6f4569b1e10d3b6292778d
timeCreated: 1708460783
@@ -0,0 +1,96 @@
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using UnityEditor;
using VOID.ScriptableObjects.Affiliation;
namespace VOID.Editor.EditorWindow.Affiliation
{
public class AffiliationEditor : OdinEditorWindow
{
[MenuItem("VOID RPG/Affiliation/Affiliation Editor")]
private static void OpenWindow() => GetWindow<AffiliationEditor>().Show();
[ShowInInspector]
[ReadOnly]
private List<Faction> _factions = new();
[ShowInInspector]
[HideReferenceObjectPicker]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, DraggableItems = false)]
private List<AffiliationEditorConflict> _conflicts = new();
[Button]
private void FindFactions()
{
_factions = AssetDatabase
.FindAssets($"t:{nameof(Faction)}", new[] { "Assets/85 - GameData" })
.Select(AssetDatabase.GUIDToAssetPath)
.Select(AssetDatabase.LoadAssetAtPath<Faction>)
.ToList();
}
[Button]
private void FindConflicts()
{
if (_factions.IsNullOrEmpty()) FindFactions();
_factions.ForEach(thisFaction =>
{
_factions.ForEach(otherFaction =>
{
CompareAttitudeConflict(thisFaction, otherFaction);
});
});
}
private void CompareAttitudeConflict(Faction faction1, Faction faction2)
{
var attitude1 = faction1.GetAttitudeFor(faction2);
var attitude2 = faction2.GetAttitudeFor(faction1);
if (attitude1 != attitude2) AddConflict(faction1, faction2);
}
private void AddConflict(Faction faction1, Faction faction2)
{
var conflictExists = _conflicts
.Where(conflict => conflict.faction1 == faction1 || conflict.faction1 == faction2)
.Any(conflict => conflict.faction2 == faction1 || conflict.faction2 == faction2);
if (conflictExists) return;
_conflicts.Add(new AffiliationEditorConflict { faction1 = faction1, faction2 = faction2 });
}
[Button]
private void ResolveEasyConflicts()
{
if (_conflicts.IsNullOrEmpty()) FindConflicts();
_conflicts.ToList().ForEach(conflict =>
{
var attitude1 = conflict.faction1.GetAttitudeFor(conflict.faction2);
var attitude2 = conflict.faction2.GetAttitudeFor(conflict.faction1);
if (attitude1.HasValue && attitude2.HasValue) return;
if (attitude1.HasValue)
{
conflict.faction1.factionAttitudes[conflict.faction2] = attitude1.Value;
conflict.faction2.factionAttitudes[conflict.faction1] = attitude1.Value;
}
if (attitude2.HasValue)
{
conflict.faction1.factionAttitudes[conflict.faction2] = attitude2.Value;
conflict.faction2.factionAttitudes[conflict.faction1] = attitude2.Value;
}
EditorUtility.SetDirty(conflict.faction1);
EditorUtility.SetDirty(conflict.faction2);
AssetDatabase.SaveAssets();
_conflicts.Remove(conflict);
});
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 26794cdf00a546adb8097ebd134eefe7
timeCreated: 1708460788
@@ -0,0 +1,51 @@
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.ScriptableObjects.Affiliation;
namespace VOID.Editor.EditorWindow.Affiliation
{
public record AffiliationEditorConflict
{
[ReadOnly]
[HorizontalGroup("faction1")]
public Faction faction1;
[ReadOnly]
[ShowInInspector]
[HideLabel]
[HorizontalGroup("faction1")]
public string faction1Attitude => faction1.GetAttitudeFor(faction2).ToString();
[ReadOnly]
[HorizontalGroup("faction2")]
public Faction faction2;
[ReadOnly]
[ShowInInspector]
[HideLabel]
[HorizontalGroup("faction2")]
public string faction2Attitude => faction2.GetAttitudeFor(faction1).ToString();
[GUIColor(nameof(GetResolveColor))]
[InlineButton(nameof(Resolve))]
public AttitudeType newAttitude;
private bool _resolved;
private void Resolve()
{
faction1.factionAttitudes[faction2] = newAttitude;
faction2.factionAttitudes[faction1] = newAttitude;
_resolved = true;
EditorUtility.SetDirty(faction1);
EditorUtility.SetDirty(faction2);
AssetDatabase.SaveAssets();
}
private Color GetResolveColor() => _resolved ? Color.green : Color.red;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7971e0e526a945848f51bd9e9eb99fad
timeCreated: 1708462802
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
namespace VOID.Editor.EditorWindow
{
public class ForceReserializeAssets : OdinEditorWindow
{
[MenuItem("VOID RPG/Assets/Force Reserialize Assets", priority = 1)]
private static void OpenWindow() => GetWindow<ForceReserializeAssets>().Show();
[InfoBox(
"<b>WHY use this:</b>\n" +
"- Update all names of properties when changed by <b>FormerlySerializedAs</b>\n" +
"\n" +
"<b>HOW use this:</b>\n" +
"- Add all files to update serialized data and press <b>Reserialize</b>\n" +
"- OR! Just press <b>Serialize Everything</b>, but it will go through all files in project"
)]
[ShowInInspector, AssetsOnly] private List<Object> _files = new();
[Button(ButtonSizes.Gigantic)]
public void Reserialize()
{
var paths = _files
.Select(AssetDatabase.GetAssetPath)
.SelectMany(file => AssetDatabase.IsValidFolder(file) ? GetAssetsPathsFromFolder(file) : new[] { file })
.Distinct()
.ToList();
AssetDatabase.ForceReserializeAssets(paths);
_files.Clear();
}
[Button(Name = "Reserialize Everything")]
[GUIColor(1, 0, 0)]
public void ReserializeEverything()
{
if (EditorUtility.DisplayDialog(
"Reserialize Everything",
"Are you sure? It'll be very long process, around ~30 minutes!",
"Yes, do it",
"No"))
{
AssetDatabase.ForceReserializeAssets();
}
}
private string[] GetAssetsPathsFromFolder(string folderPath)
{
return AssetDatabase.FindAssets("", new[] { folderPath }).Select(AssetDatabase.GUIDToAssetPath).ToArray();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5964811217cf4e80b131270d1c11cad2
timeCreated: 1727892808
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e84a6e0b5acc40088df9f749e3994c87
timeCreated: 1691598777
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
using VOID.Generic.Objects.Item;
namespace VOID.Editor.EditorWindow.Objects
{
public class ItemUniquenessGenerator : OdinEditorWindow
{
[ShowInInspector, ReadOnly] private List<GameObject> _wereDuplicated = new();
[ShowInInspector, ReadOnly] private List<GameObject> _wereMissing = new();
[ShowInInspector, ReadOnly] private List<GameObject> _untouched = new();
[MenuItem("VOID RPG/Items/UniqueID generator")]
private static void OpenWindow() => GetWindow<ItemUniquenessGenerator>().Show();
[Button("GENERATE")]
public void Generate()
{
EditorUtility.DisplayProgressBar("Generating unique ids...", "PREPARING", 0);
_wereDuplicated.Clear();
_wereMissing.Clear();
_untouched.Clear();
var stopwatch = Stopwatch.StartNew();
var usedUniqueIds = new List<string>();
var guids = AssetDatabase.FindAssets("t:prefab", new[] { "Assets/2 - Prefabs" });
for (var i = 0; i < guids.Length; i++)
{
EditorUtility.DisplayProgressBar("Generating unique ids...", $"{i} / {guids.Length}", (float)i / guids.Length);
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (PrefabUtility.GetPrefabAssetType(prefab) is not PrefabAssetType.Regular) continue;
var item = prefab.GetComponent<ItemObject>();
if (!item) continue;
if (item.itemData.uniqueId.IsNullOrWhitespace())
{
// Missing UniqueId
_wereMissing.Add(prefab);
item.itemData.uniqueId = Guid.NewGuid().ToString();
}
else if (usedUniqueIds.Contains(item.itemData.uniqueId))
{
// Duplicated UniqueId
_wereDuplicated.Add(prefab);
item.itemData.uniqueId = Guid.NewGuid().ToString();
}
else
{
// Looks good
_untouched.Add(prefab);
}
usedUniqueIds.Add(item.itemData.uniqueId);
PrefabUtility.SaveAsPrefabAsset(prefab, AssetDatabase.GetAssetPath(prefab));
}
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog(
"Generating UniqueIDs finished",
$"Generating finished in {stopwatch.ElapsedMilliseconds} ms.\n" +
$"{_wereMissing.Count} - ItemObjects had missing UniqueID.\n" +
$"{_wereDuplicated.Count} - ItemObjects had duplicated UniqueID.\n" +
$"{_untouched.Count} - ItemObjects had good UniqueID.",
"OK"
);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40849949eefb43a699aeb43c1ed0e681
timeCreated: 1691598803
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5b88ecf75a0a68d4097bf76407b5bf22
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,350 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
using VOID.Editor.Utils;
using VOID.Generic.World;
namespace VOID.Editor.EditorWindow.World
{
public class ComplexAreaRotateVariantGenerator : OdinEditorWindow
{
[MenuItem("VOID RPG/World/Complex Area Rotate Variant Generator")]
private static void OpenWindow() => GetWindow<ComplexAreaRotateVariantGenerator>().Show();
// Input and Output for user
[ShowInInspector, AssetsOnly] private List<ComplexArea> sourceComplexAreas = new();
[ShowInInspector, ReadOnly] private List<ComplexArea> generatedComplexAreas = new();
// Rotation variants to create
private static readonly float[] Rotations = { 90, 180, 270 };
// Temporary variables
private ComplexArea _targetComplexArea;
private string _sourcePrefabPath;
private string _sourceFolder;
private float _variantRotation;
private string _targetPrefabPath;
private string _targetFolder;
public static void GenerateRotatedVariants(ComplexArea complexArea)
{
// Only ASSETS for this generation!
var prefab = complexArea.gameObject.GetPrefabAsset();
if (prefab == null)
{
Debug.LogError("Only prefab asset is valid to variant generation!");
return;
}
// Just to be sure - save given prefab for user before generation
PrefabUtility.SavePrefabAsset(complexArea.gameObject);
var isOpen = HasOpenInstances<ComplexAreaRotateVariantGenerator>();
var variantGenerator = GetWindow<ComplexAreaRotateVariantGenerator>(false, null, false);
variantGenerator.sourceComplexAreas = new List<ComplexArea> { prefab.GetComponent<ComplexArea>() };
variantGenerator.GenerateRotatedVariants();
if (!isOpen) variantGenerator.Close();
}
[Button]
private void GenerateRotatedVariants()
{
if (sourceComplexAreas.IsNullOrEmpty())
{
Debug.LogWarning("ComplexArea not selected");
return;
}
generatedComplexAreas.Clear();
sourceComplexAreas.ForEach(sourceComplexArea =>
{
var variants = Rotations.Select(rotation => GenerateRotatedVariant(sourceComplexArea, rotation)).ToList();
sourceComplexArea.isVariant = false;
sourceComplexArea.variants = variants;
sourceComplexArea.original = null;
PrefabUtility.SavePrefabAsset(sourceComplexArea.gameObject);
generatedComplexAreas.AddRange(variants);
});
sourceComplexAreas.Clear();
}
private ComplexArea GenerateRotatedVariant(ComplexArea sourceComplexArea, float rotation)
{
_sourcePrefabPath = AssetDatabase.GetAssetPath(sourceComplexArea.gameObject);
_sourceFolder = Path.GetDirectoryName(_sourcePrefabPath)?.Replace("\\", "/") ?? "";
_variantRotation = rotation;
_targetFolder = $"{_sourceFolder}/{sourceComplexArea.gameObject.name}_v{_variantRotation}";
if (Directory.Exists(_targetFolder)) Directory.Delete(_targetFolder, true);
Directory.CreateDirectory(_targetFolder);
DuplicatePrefab();
DuplicateTerrains();
RotatePrefab();
UpdateTerrainsHeight();
UpdateTerrainsAlphas();
UpdateTerrainsDetails();
UpdateTerrainsTrees();
UpdateComplexArea();
UpdateComplexAreaOriginalVariant(sourceComplexArea);
PrefabUtility.SaveAsPrefabAsset(_targetComplexArea.gameObject, _targetPrefabPath);
PrefabUtility.UnloadPrefabContents(_targetComplexArea.gameObject);
return AssetDatabase.LoadAssetAtPath<GameObject>(_targetPrefabPath).GetComponent<ComplexArea>();
}
private void DuplicatePrefab()
{
// Duplicate prefab asset in sub directory
_targetPrefabPath = $"{_targetFolder}/{Path.GetFileNameWithoutExtension(_sourcePrefabPath)}_v{_variantRotation}.prefab";
AssetDatabase.CopyAsset(_sourcePrefabPath, _targetPrefabPath);
// "Open" duplicated prefab for editing
_targetComplexArea = PrefabUtility.LoadPrefabContents(_targetPrefabPath).GetComponent<ComplexArea>();
}
private void DuplicateTerrains()
{
// Duplicate TerrainData and place them in relative directiory (if not possible put them next to prefab)
_targetComplexArea.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var dataPath = AssetDatabase.GetAssetPath(terrain.terrainData);
var dataPathNew = GetNewAssetPath(dataPath);
Directory.CreateDirectory(_targetFolder);
AssetDatabase.CopyAsset(dataPath, dataPathNew);
var terrainData = AssetDatabase.LoadAssetAtPath<TerrainData>(dataPathNew);
terrain.GetComponent<Terrain>().terrainData = terrainData;
terrain.GetComponent<TerrainCollider>().terrainData = terrainData;
});
// Update asset database
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private void RotatePrefab()
{
// Terrain rotating FIX - instead we will rotate dummies
var terrainPositionDummyList = new Dictionary<GameObject, Terrain>();
_targetComplexArea.gameObject.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var dummy = new GameObject();
var sizeOffset = Vector3.Scale(new Vector3(.5f, 0, .5f), terrain.terrainData.size);
dummy.transform.parent = terrain.transform.parent;
dummy.transform.position = terrain.transform.position + sizeOffset;
terrainPositionDummyList.Add(dummy, terrain);
});
// All children of prefab
var prefabChildren = new List<Transform>();
for (var i = 0; i < _targetComplexArea.gameObject.transform.childCount; i++)
prefabChildren.Add(_targetComplexArea.gameObject.transform.GetChild(i));
// Put empty dummy
var dummyRotate = new GameObject();
dummyRotate.transform.parent = _targetComplexArea.gameObject.transform;
dummyRotate.transform.position = Vector3.zero;
// Put all children into dummy
prefabChildren.ForEach(child => child.parent = dummyRotate.transform);
// Rotate dummy with all children
dummyRotate.transform.Rotate(Vector3.up, _variantRotation);
// Take out all children from dummy
prefabChildren.ForEach(child => child.parent = _targetComplexArea.gameObject.transform);
// Remove dummy
DestroyImmediate(dummyRotate);
// Terrain rotating FIX - Put terrains in good positions
terrainPositionDummyList.ForEach(pair =>
{
var dummy = pair.Key;
var terrain = pair.Value;
var sizeOffset = Vector3.Scale(new Vector3(.5f, 0, .5f), terrain.terrainData.size);
terrain.transform.position = pair.Key.transform.position - sizeOffset;
terrain.transform.rotation = Quaternion.identity;
DestroyImmediate(pair.Key);
});
// Update asset database
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private void UpdateTerrainsHeight()
{
_targetComplexArea.gameObject.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var rotationTimes = Mathf.RoundToInt(_variantRotation / 90);
var terrainData = terrain.terrainData;
var heightMapSize = terrainData.heightmapResolution;
var heights = terrainData.GetHeights(0, 0, heightMapSize, heightMapSize);
var rotatedHeights = new float[heightMapSize, heightMapSize];
// Perform rotating N times
for (var i = 0; i < rotationTimes; i++)
{
// Rotating whole two-dimensional height array by 90 degree
for (var y = 0; y < heightMapSize; y++)
for (var x = 0; x < heightMapSize; x++)
rotatedHeights[y, x] = heights[x, heightMapSize - y - 1];
// Rotated by 90 degree, prepare starting point for next rotating
heights = (float[,])rotatedHeights.Clone();
}
// Update terrain
terrainData.SetHeights(0, 0, rotatedHeights);
});
}
private void UpdateTerrainsAlphas()
{
_targetComplexArea.gameObject.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var rotationTimes = Mathf.RoundToInt(_variantRotation / 90);
var terrainData = terrain.terrainData;
var alphaSize = terrainData.alphamapResolution;
var alphaCount = terrainData.alphamapLayers;
var alphas = terrainData.GetAlphamaps(0, 0, alphaSize, alphaSize);
var rotatedAlphas = new float[alphaSize, alphaSize, alphaCount];
// Perform rotating N times
for (var i = 0; i < rotationTimes; i++)
{
// Rotating whole two-dimensional array for each alpha map by 90 degree
for (var y = 0; y < alphaSize; y++)
for (var x = 0; x < alphaSize; x++)
for (var a = 0; a < alphaCount; a++)
rotatedAlphas[y, x, a] = alphas[x, alphaSize - y - 1, a];
// Rotated by 90 degree, prepare starting point for next rotating
alphas = (float[,,])rotatedAlphas.Clone();
}
// Update terrain
terrainData.SetAlphamaps(0, 0, rotatedAlphas);
});
}
private void UpdateTerrainsDetails()
{
_targetComplexArea.gameObject.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var rotationTimes = Mathf.RoundToInt(_variantRotation / 90);
var terrainData = terrain.terrainData;
var detailSize = terrainData.detailResolution;
var detailLayerCount = terrainData.detailPrototypes.Length;
var rotatedDetails = new int[detailSize, detailSize];
// Rotate ALL detail layers
for (var a = 0; a < detailLayerCount; a++)
{
// Get current detail layer at index
var details = terrainData.GetDetailLayer(0, 0, detailSize, detailSize, a);
// Perform rotating N times
for (var i = 0; i < rotationTimes; i++)
{
// Rotating whole dimensional array by 90 degree
for (var y = 0; y < detailSize; y++)
for (var x = 0; x < detailSize; x++)
rotatedDetails[y, x] = details[x, detailSize - y - 1];
// Rotated by 90 degree, prepare starting point for next rotating
details = (int[,])rotatedDetails.Clone();
}
// Update terrain
terrainData.SetDetailLayer(0, 0, a, rotatedDetails);
}
});
}
private void UpdateTerrainsTrees()
{
_targetComplexArea.gameObject.GetComponentsInChildren<Terrain>().ForEach(terrain =>
{
var terrainData = terrain.terrainData;
// New empty GameObject for further rotating trees
var terrainDummy = new GameObject();
terrainDummy.transform.position = terrain.transform.position + terrainData.size/2;
// Map dummy GameObjects to each tree for rotating purposes, by making it child to terrain dummy
var treeList = terrainData.treeInstances;
var dummyList = new GameObject[treeList.Length];
for (var i = 0; i < treeList.Length; i++)
{
dummyList[i] = new GameObject();
dummyList[i].transform.parent = terrainDummy.transform;
dummyList[i].transform.position = terrain.transform.position + Vector3.Scale(treeList[i].position, terrainData.size);
}
// Rotate dummy terrain (and all tree dummies)
terrainDummy.transform.Rotate(Vector3.up, _variantRotation);
// Corrent positions and rotations of all trees, by using created dummies
for (var i = 0; i < treeList.Length; i++)
{
var relativePosition = dummyList[i].transform.position - terrain.transform.position;
treeList[i].position = new Vector3(relativePosition.x / terrainData.size.x, 0, relativePosition.z / terrainData.size.z);
treeList[i].rotation = dummyList[i].transform.rotation.eulerAngles.y;
}
// Update Terrain
terrainData.SetTreeInstances(treeList, true);
// Remove dummies
dummyList.ForEach(DestroyImmediate);
DestroyImmediate(terrainDummy);
});
}
private void UpdateComplexArea()
{
_targetComplexArea.areas.ForEach(area =>
{
// Areas should not be rotated
area.transform.rotation = Quaternion.identity;
// Swap connection clockwise - N times of needed rotation
var rotationTimes = Mathf.RoundToInt(_variantRotation / 90);
for (var i = 0; i < rotationTimes; i++)
{
var tempConnection = area.forwardConnection;
area.forwardConnection = area.leftConnection;
area.leftConnection = area.backConnection;
area.backConnection = area.rightConnection;
area.rightConnection = tempConnection;
}
});
// Run all inside updates and fixes
_targetComplexArea.UpdateAll();
_targetComplexArea.AutoFixAll();
}
private void UpdateComplexAreaOriginalVariant(ComplexArea sourceComplexArea)
{
_targetComplexArea.isVariant = true;
_targetComplexArea.variants.Clear();
_targetComplexArea.original = sourceComplexArea;
}
private string GetNewAssetPath(string path)
{
if (path.StartsWith(_sourceFolder) == false) return $"{_targetFolder}/{Path.GetFileName(path)}";
var relativePath = path.Substring(_sourceFolder.Length).TrimStart('/');
return $"{_targetFolder}/{relativePath}";
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f3e35552b4d52364b8ddd710111b0378
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 84924921c06640c09e087f66790ff91d
timeCreated: 1723292313
@@ -0,0 +1,28 @@
using UnityEditor;
using UnityEngine;
namespace VOID.Editor.PropertyDrawers
{
[CustomPropertyDrawer(typeof(SceneReference))]
public class SceneFieldPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(rect, GUIContent.none, property);
var sceneAsset = property.FindPropertyRelative("_sceneAsset");
var scenePath = property.FindPropertyRelative("_scenePath");
// Label + Field
rect = EditorGUI.PrefixLabel(rect, GUIUtility.GetControlID(FocusType.Passive), label);
var obj = EditorGUI.ObjectField(rect, sceneAsset.objectReferenceValue, typeof(SceneAsset), false);
// Value setter
sceneAsset.objectReferenceValue = obj;
scenePath.stringValue = obj ? AssetDatabase.GetAssetPath(obj) : "";
EditorGUI.EndProperty();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f070454f4a8ab6b4a8699e458a53d2d2
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ae753208adfe4827ac35aaab9d0c142c
timeCreated: 1716668852
@@ -0,0 +1,17 @@
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace VOID.Editor.Utils
{
public static class GameObjectExtensions
{
public static GameObject GetPrefabAsset(this GameObject gameObject)
{
var prefab = AssetDatabase.IsMainAsset(gameObject) ? gameObject : null;
if (prefab == null) prefab = PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject);
if (prefab == null) prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabStageUtility.GetPrefabStage(gameObject)?.assetPath);
return prefab;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf01bc372a8d48e9b16b33e3bd4c09fe
timeCreated: 1716668862
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4db31651f8f74ae4af5cfcaf35e5d8eb
timeCreated: 1717875873
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using VOID.Generic.DropTable;
namespace VOID.Editor.ValueDrawers
{
[DrawerPriority(DrawerPriorityLevel.SuperPriority)]
public class DropTableGroupDrawer : OdinValueDrawer<DropTableGroup>
{
protected override void DrawPropertyLayout(GUIContent label)
{
var parent = ValueEntry?.Property?.Parent?.ValueEntry?.WeakSmartValue;
if (parent is List<DropTableGroup> list)
{
var percent = ValueEntry.SmartValue.weight / list.Sum(group => group.weight);
var colorProgress = ValueEntry.SmartValue.weight / list.Max(group => group.weight);
var style = new GUIStyle();
style.alignment = TextAnchor.MiddleCenter;
style.fontStyle = FontStyle.Bold;
style.normal.textColor = Color.white;
style.normal.background = GetBackgroundColor(Color.Lerp(Color.red, Color.green, colorProgress));
EditorGUILayout.BeginHorizontal();
GUILayout.Box($"{Mathf.FloorToInt(percent*100)}%", style, GUILayout.Width(35), GUILayout.ExpandHeight(true));
EditorGUILayout.BeginVertical();
CallNextDrawer(label);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
else
{
CallNextDrawer(label);
}
}
private static Texture2D GetBackgroundColor(Color color)
{
var texture = new Texture2D(1, 1);
texture.SetPixel(0, 0, color);
texture.Apply();
return texture;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3155746e62494e46adf5d94c5c16eb8d
timeCreated: 1717875913
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using VOID.ScriptableObjects.World;
namespace VOID.Editor.ValueDrawers
{
[DrawerPriority(DrawerPriorityLevel.SuperPriority)]
public class WorldAreaGeneratorSettingsDrawer : OdinValueDrawer<WorldAreaGeneratorSettings>
{
protected override void DrawPropertyLayout(GUIContent label)
{
var parent = ValueEntry?.Property?.Parent?.ValueEntry?.WeakSmartValue;
if (parent is List<WorldAreaGeneratorSettings> list)
{
var percent = ValueEntry.SmartValue.weight / list.Sum(group => group.weight);
var colorProgress = ValueEntry.SmartValue.weight / list.Max(group => group.weight);
var style = new GUIStyle();
style.alignment = TextAnchor.MiddleCenter;
style.fontStyle = FontStyle.Bold;
style.normal.textColor = Color.white;
style.normal.background = GetBackgroundColor(Color.Lerp(Color.red, Color.green, colorProgress));
EditorGUILayout.BeginHorizontal();
GUILayout.Box($"{Mathf.FloorToInt(percent*100)}%", style, GUILayout.Width(35), GUILayout.ExpandHeight(true));
EditorGUILayout.BeginVertical();
CallNextDrawer(label);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
}
else
{
CallNextDrawer(label);
}
}
private static Texture2D GetBackgroundColor(Color color)
{
var texture = new Texture2D(1, 1);
texture.SetPixel(0, 0, color);
texture.Apply();
return texture;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 72d3774e8ae1409aa8bad15b48bfb40c
timeCreated: 1717932923
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a8052a465d54c08bb17a3fea4b7a230
timeCreated: 1716642054
@@ -0,0 +1,56 @@
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.ScriptableObjects.Animations;
[assembly: RegisterValidator(typeof(AnimationListValidator))]
namespace VOID.Editor.AttributeValidator
{
public class ActionAnimationListValidator : ValueValidator<ActionAnimationList>
{
private static readonly string[] SimpleEvents = { "OnStart", "OnPerform", "OnEnd" };
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidateClipEvents(result, Value.list.clip, SimpleEvents);
}
private void ValidateClipEvents(ValidationResult result, AnimationClip clip, string[] neededEvents)
{
if (!clip) return;
var currentEvents = clip.events.Select(ev => ev.functionName).ToList();
var missingEvents = neededEvents.Except(currentEvents).ToList();
var invalidEvents = currentEvents.Except(neededEvents).ToList();
var duplicatedEvents = currentEvents.GroupBy(s => s).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (missingEvents.Count > 0)
{
var missingEventsText = string.Join(" ", missingEvents.Select(str => $"<u>{str}</u>"));
result
.AddError($"<u>{clip.name}</u> is missing required events: " + missingEventsText)
.SetSelectionObject(clip);
}
if (invalidEvents.Count > 0)
{
var invalidEventsText = string.Join(" ", invalidEvents.Select(str => $"<u>{str}</u>"));
result
.AddWarning($"<u>{clip.name}</u> contain unknown events: " + invalidEventsText)
.SetSelectionObject(clip);
}
if (duplicatedEvents.Count > 0)
{
var duplicatedEventsText = string.Join(" ", duplicatedEvents.Select(str => $"<u>{str}</u>"));
result
.AddError($"<u>{clip.name}</u> contain duplicated events: " + duplicatedEventsText)
.SetSelectionObject(clip);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2d25375d33f40e0bb0cf42d99e0ef3e
timeCreated: 1719581332
@@ -0,0 +1,73 @@
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.ScriptableObjects.Animations;
[assembly: RegisterValidator(typeof(AnimationListValidator))]
namespace VOID.Editor.AttributeValidator
{
public class AnimationListValidator : ValueValidator<ActionLoopAnimationList>
{
private static readonly string[] LoopStartEvents = { "OnStart", "OnLoopStart", "OnPerform" };
private static readonly string[] LoopEvents = {};
private static readonly string[] LoopEndEvents = { "OnLoopEnd", "OnEnd" };
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidateClipEvents(result, Value.list.startClip, LoopStartEvents);
ValidateClipEvents(result, Value.list.loopClip, LoopEvents);
ValidateClipEvents(result, Value.list.endClip, LoopEndEvents);
ValidateStartEndClips(result);
}
private void ValidateClipEvents(ValidationResult result, AnimationClip clip, string[] neededEvents)
{
if (!clip) return;
var currentEvents = clip.events.Select(ev => ev.functionName).ToList();
var missingEvents = neededEvents.Except(currentEvents).ToList();
var invalidEvents = currentEvents.Except(neededEvents).ToList();
var duplicatedEvents = currentEvents.GroupBy(s => s).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (missingEvents.Count > 0)
{
var missingEventsText = string.Join(" ", missingEvents.Select(str => $"<u>{str}</u>"));
result
.AddError($"<u>{clip.name}</u> is missing required events: " + missingEventsText)
.SetSelectionObject(clip);
}
if (invalidEvents.Count > 0)
{
var invalidEventsText = string.Join(" ", invalidEvents.Select(str => $"<u>{str}</u>"));
result
.AddWarning($"<u>{clip.name}</u> contain unknown events: " + invalidEventsText)
.SetSelectionObject(clip);
}
if (duplicatedEvents.Count > 0)
{
var duplicatedEventsText = string.Join(" ", duplicatedEvents.Select(str => $"<u>{str}</u>"));
result
.AddError($"<u>{clip.name}</u> contain duplicated events: " + duplicatedEventsText)
.SetSelectionObject(clip);
}
}
private void ValidateStartEndClips(ValidationResult result)
{
if (!Value.list.startClip && !Value.list.endClip) return;
var subInfo = $"Both {nameof(Value.list.startClip)} and {nameof(Value.list.endClip)} should exists or be deleted!";
if (!Value.list.startClip)
result.AddError($"<u>{nameof(Value.list.startClip)}</u> does not exists!\n{subInfo}");
if (!Value.list.endClip)
result.AddError($"<u>{nameof(Value.list.endClip)}</u> does not exists!\n{subInfo}");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f202a516ff864e749792bc74d6cc50b0
timeCreated: 1743195641
@@ -0,0 +1,52 @@
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.Generic.World;
[assembly: RegisterValidator(typeof(AreaValidator))]
namespace VOID.Editor.AttributeValidator
{
public class AreaValidator : ValueValidator<Area>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
// Validate only ASSET - skip scene
if (!Value.gameObject.scene.path.IsNullOrWhitespace()) return;
ValidateIsInComplexAreaPrefab(result);
ValidatePosition(result);
}
private void ValidateIsInComplexAreaPrefab(ValidationResult result)
{
if (Value.transform.root.gameObject.GetComponent<ComplexArea>() == null)
{
result.AddError(
$"{nameof(Area)} needs to be placed inside prefab with component {nameof(ComplexArea)}!"
);
}
}
private void ValidatePosition(ValidationResult result)
{
var complexArea = Value.transform.root.gameObject.GetComponent<ComplexArea>();
if (!complexArea) return;
var relativePosition = complexArea.transform.position - Value.transform.position;
var xValid = relativePosition.x % Area.Size < Vector3.kEpsilon;
var yValid = relativePosition.y % Area.Size < Vector3.kEpsilon;
var zValid = relativePosition.z % Area.Size < Vector3.kEpsilon;
if (!xValid || !yValid || !zValid)
{
result.AddError(
$"'{Value.name}' is placed in invalid position."
+ $"\nRelative position to ComplexArea is {relativePosition} and is not dividable by {Area.Size}!"
);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a486a4fce9d4fe1a3d9ca9aa04d29c5
timeCreated: 1716645645
@@ -0,0 +1,42 @@
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.Generic;
using VOID.Generic.Triggers;
[assembly: RegisterValidator(typeof(CameraTriggerValidator))]
namespace VOID.Editor.AttributeValidator
{
public class CameraTriggerValidator : ValueValidator<ICameraTrigger>
{
private Component _component;
private GameObject _gameObject;
protected override void Validate(ValidationResult result)
{
if (Value is not Component component) return;
_component = component;
_gameObject = _component.gameObject;
ValidateLayer(result);
ValidateIsTrigger(result);
}
private void ValidateLayer(ValidationResult result)
{
if (LayerManager.CodeInMask(_component.gameObject.layer, LayerManager.Camera)) return;
result.AddError($"Layer is not <u>{nameof(LayerManager.Camera)}</u>!");
}
private void ValidateIsTrigger(ValidationResult result)
{
var isTrigger = _gameObject.GetComponents<Collider>().All(c => c.isTrigger);
if (isTrigger) return;
result.AddError("All <u>Is Trigger</u> in colliders must be set to <u>true</u>!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 548b1cf469c446009821a3734df0d267
timeCreated: 1731686163
@@ -0,0 +1,66 @@
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.Generic.World;
[assembly: RegisterValidator(typeof(ComplexAreaValidator))]
namespace VOID.Editor.AttributeValidator
{
public class ComplexAreaValidator : ValueValidator<ComplexArea>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
// Validate only ASSET - skip scene
if (!Value.gameObject.scene.path.IsNullOrWhitespace()) return;
ValidateIsInRootPrefab(result);
ValidatePosition(result);
ValidateIsAtLeastOneAreaDefined(result);
ValidateAreAllAreasHandled(result);
}
private void ValidateIsInRootPrefab(ValidationResult result)
{
var prefabRoot = Value.transform.root.gameObject;
if (prefabRoot != Value.gameObject)
{
result.AddError($"{nameof(ComplexArea)} is not in prefab root!");
}
}
private void ValidatePosition(ValidationResult result)
{
if (Value.gameObject.transform.localPosition != Vector3.zero)
{
result.AddError($"{nameof(ComplexArea)}'s position in prefab must be {Vector3.zero}!");
}
}
private void ValidateIsAtLeastOneAreaDefined(ValidationResult result)
{
var areas = Value.areas;
if (areas.IsNullOrEmpty())
{
result.AddError($"{nameof(ComplexArea)} does not have any {nameof(Area)} defined!");
}
}
private void ValidateAreAllAreasHandled(ValidationResult result)
{
var areas = Value.areas;
var areasInPrefab = Value.transform.root.GetComponentsInChildren<Area>().ToList();
var areasDiff = areasInPrefab.Except(areas).ToList();
if (!areasDiff.IsNullOrEmpty())
{
result.AddError($"{nameof(ComplexArea)} does not contain all {nameof(Area)} defined in this prefab!");
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b8f5c7642b3c44009be0fb9d35dc02b3
timeCreated: 1716642064
@@ -0,0 +1,53 @@
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using VOID.Editor.AttributeValidator;
using VOID.Generic.DropTable;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Container;
using VOID.Generic.Objects.Unit;
[assembly: RegisterValidator(typeof(DropTableComponentValidator))]
namespace VOID.Editor.AttributeValidator
{
public class DropTableComponentValidator : ValueValidator<DropTableComponent>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
var attachedTo = Value.GetComponent<AbstractObject>();
IsPlacedWithAbstractObject(result, attachedTo);
if (attachedTo == null) return;
CanUseTriggerOnSpawn(result, attachedTo);
IsDropTableDefined(result);
}
private void IsPlacedWithAbstractObject(ValidationResult result, AbstractObject attachedTo)
{
// DropTable's GameObject also needs to have AbstractObject (or its children)
if (attachedTo != null) return;
result.AddError($"{nameof(DropTableComponent)} needs to be placed together with any <u><b>{nameof(AbstractObject)}</b></u>");
}
private void CanUseTriggerOnSpawn(ValidationResult result, AbstractObject attachedTo)
{
// OnSpawn can be used only with Unit or Container
if (Value.trigger is not DropTableTrigger.OnSpawn || attachedTo is UnitObject or ContainerObject) return;
result.AddError($"{nameof(DropTableComponent)}'s {nameof(Value.trigger)}: <u><b>{Value.trigger}</b></u> can't be used with <u><b>{attachedTo.GetType().GetNiceName()}</b></u>");
}
private void IsDropTableDefined(ValidationResult result)
{
// DropTable is required!
if (Value.dropTable != null) return;
result.AddError($"{nameof(DropTableComponent)}'s drop table is not defined!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0576c79013184fe3beb6fa3238f90302
timeCreated: 1717863063
@@ -0,0 +1,26 @@
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using VOID.Editor.AttributeValidator;
using VOID.Generic.DynamicValue;
[assembly: RegisterValidator(typeof(DynamicFormulaValidator))]
namespace VOID.Editor.AttributeValidator
{
public class DynamicFormulaValidator : ValueValidator<DynamicFormula>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidatePresent(result);
}
private void ValidatePresent(ValidationResult result)
{
if (!Value.dynamicValue.IsNullOrWhitespace()) return;
result.AddError("Math formula is empty!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a7a4957ce3047b69d9a190f18b142c0
timeCreated: 1723921186
@@ -0,0 +1,18 @@
using Sirenix.OdinInspector.Editor.Validation;
using VOID.Editor.AttributeValidator;
using VOID.Generic.DynamicValue;
[assembly: RegisterValidator(typeof(DynamicTextValidator))]
namespace VOID.Editor.AttributeValidator
{
public class DynamicTextValidator : ValueValidator<DynamicText>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
// TODO: walidacja znaczników
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3cbdf6c4f9bd462dbe91d5cb533628f0
timeCreated: 1723838013
@@ -0,0 +1,37 @@
using Sirenix.OdinInspector.Editor.Validation;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.Generic;
using VOID.Generic.Triggers;
[assembly: RegisterValidator(typeof(DynamicTriggerValidator))]
namespace VOID.Editor.AttributeValidator
{
public class DynamicTriggerValidator : ValueValidator<IDynamicTrigger>
{
protected override void Validate(ValidationResult result)
{
if (Value is not Component component) return;
ValidateLayer(result, component);
ValidateCollider(result, component);
}
private void ValidateLayer(ValidationResult result, Component component)
{
if (LayerManager.CodeInMask(component.gameObject.layer, LayerManager.Trigger)) return;
result.AddError(
$"This component uses <u>{nameof(IDynamicTrigger)}</u> which requires layer <u>{nameof(LayerManager.Trigger)}</u>!");
}
private void ValidateCollider(ValidationResult result, Component component)
{
if (component.GetComponentInChildren<Collider>()) return;
result.AddError(
$"This component uses <u>{nameof(IDynamicTrigger)}</u> which requires at least one <u>collider</u>!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edf8c57dfd744b9ca332155888b9677d
timeCreated: 1731687244
@@ -0,0 +1,32 @@
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using UnityEngine;
using VOID.Editor.AttributeValidator;
using VOID.Generic;
using VOID.Generic.Triggers;
[assembly: RegisterValidator(typeof(HideCeilingTriggerValidator))]
namespace VOID.Editor.AttributeValidator
{
public class HideCeilingTriggerValidator : ValueValidator<HideCeilingTrigger>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidateHideableLayer(result);
}
private void ValidateHideableLayer(ValidationResult result)
{
var isWrongLayer = Value.gameObjectsToHide.SelectMany(go => go.GetComponentsInChildren<Transform>())
.Select(transform => transform.gameObject.layer)
.Any(layer => layer != LayerManager.StaticHideableIndex);
if (!isWrongLayer) return;
result.AddError($"All hideable objects targeted by this components needs layer <u>{nameof(LayerManager.StaticHideable)}</u>");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b19a84b828dc44d19acc91a3562527f6
timeCreated: 1747502208
@@ -0,0 +1,25 @@
using InfinityPBR.ModularCharacterHelper;
using Sirenix.OdinInspector.Editor.Validation;
using VOID.Editor.AttributeValidator;
[assembly: RegisterValidator(typeof(ModularCharacterValidator))]
namespace VOID.Editor.AttributeValidator
{
public class ModularCharacterValidator : ValueValidator<ModularCharacter>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidateBoneRoot(result);
}
private void ValidateBoneRoot(ValidationResult result)
{
if (Value.targetRootBone) return;
result.AddError($"<b>{nameof(Value.targetRootBone)}</b> is required!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2174b4dd14e6457f91bf2752538551b6
timeCreated: 1724879981
@@ -0,0 +1,33 @@
using InfinityPBR.ModularCharacterHelper;
using Sirenix.OdinInspector.Editor.Validation;
using VOID.Editor.AttributeValidator;
[assembly: RegisterValidator(typeof(ModularPartValidator))]
namespace VOID.Editor.AttributeValidator
{
public class ModularPartValidator : ValueValidator<ModularPart>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
ValidateBoneRoot(result);
ValidateSkinnedRenderer(result);
}
private void ValidateBoneRoot(ValidationResult result)
{
if (Value.boneRoot) return;
result.AddError($"<b>{nameof(Value.boneRoot)}</b> is required!");
}
private void ValidateSkinnedRenderer(ValidationResult result)
{
if (Value.skinnedMeshRenderer) return;
result.AddError($"<b>{nameof(Value.skinnedMeshRenderer)}</b> is required!");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b10c2b7729514d839a70884069ed6c0e
timeCreated: 1724870308
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f58b0acec154c95a69320f5a8c74317
timeCreated: 1725482081
@@ -0,0 +1,40 @@
using System;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Editor.AttributeValidator.Objects;
using VOID.Generic.Objects.Abstract;
[assembly: RegisterValidator(typeof(AbstractObjectValidator))]
namespace VOID.Editor.AttributeValidator.Objects
{
public class AbstractObjectValidator : ValueValidator<AbstractObject>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
if (OdinPrefabUtility.GetPrefabKind(Value.gameObject) is not PrefabKind.Regular) return;
IsComponentOrderValid(result);
}
private void IsComponentOrderValid(ValidationResult result)
{
var components = Value.gameObject.GetComponents<Component>();
var abstractObjectIndex = Array.FindIndex(components, component => component is AbstractObject);
// *Object is first (or right after Transform)
if (abstractObjectIndex <= 1) return;
result.AddError($"{Value.GetType().GetNiceName()} should be first component (right after Transform)")
.WithFix(() =>
{
for (var i = 0; i < abstractObjectIndex - 1; i++)
UnityEditorInternal.ComponentUtility.MoveComponentUp(components[abstractObjectIndex]);
});
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d353447115784aa3806a3b1582e00469
timeCreated: 1744384089
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Editor.AttributeValidator.Objects;
using VOID.Generic.Objects.Item;
using VOID.Generic.Objects.Usable;
[assembly: RegisterValidator(typeof(ItemObjectDataValidator))]
namespace VOID.Editor.AttributeValidator.Objects
{
public class ItemObjectDataValidator : ValueValidator<ItemObjectData>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
if (Property.Parent.ValueEntry.WeakSmartValue is not ItemObject itemObject) return;
if (OdinPrefabUtility.GetPrefabKind(itemObject.gameObject) is not PrefabKind.Regular) return;
IsStackableValid(result);
}
private void IsStackableValid(ValidationResult result)
{
var allowedTypes = new List<Type> { typeof(ItemObject), typeof(UsableObject) };
if (!Value.stackable || allowedTypes.Contains(Property.ParentType)) return;
var allowedTypeNames = string.Join(", ", allowedTypes.Select(t => t.GetNiceName()).ToList());
result.AddError($"Stackable can be TRUE only for objects of type: {allowedTypeNames}")
.WithFix(() => Value.stackable = false);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 665bab4b4bfc4f829cb3b4738084cdde
timeCreated: 1744214861
@@ -0,0 +1,34 @@
using System;
using System.Linq;
using Sirenix.OdinInspector.Editor.Validation;
using VOID.Editor.AttributeValidator.Objects;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
[assembly: RegisterValidator(typeof(UnitObjectEquipmentValidator))]
namespace VOID.Editor.AttributeValidator.Objects
{
public class UnitObjectEquipmentValidator : ValueValidator<UnitObjectEquipment>
{
protected override void Validate(ValidationResult result)
{
if (Value == null) return;
AreValidWearablesPlaced(result);
}
private void AreValidWearablesPlaced(ValidationResult result)
{
foreach (var slotType in Enum.GetValues(typeof(EquipmentSlotType)).Cast<EquipmentSlotType>())
{
var wearable = Value.GetEquipped(slotType);
if (wearable == null) return;
// Wrong type of wearable placed in this slot
if (Value.CanEquipInSlot(slotType, wearable) == false)
result.AddError($"<u><b>{wearable.name}</b></u> can't be placed in <u><b>{slotType}</b></u> slot!");
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edb7019ba7024f92b3aad84d20f3431b
timeCreated: 1718794692
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector.Editor.Validation;
using VOID.Editor.AttributeValidator;
[assembly: RegisterValidator(typeof(SceneReferenceValidator))]
namespace VOID.Editor.AttributeValidator
{
public class SceneReferenceValidator : ValueValidator<SceneReference>
{
protected override void Validate(ValidationResult result)
{
if (ValueEntry.SmartValue == "") result.AddError(Property.NiceName + " is required");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c3223426e38404fb5bea6a8db202522
timeCreated: 1730239681