init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6b3999cf3b860649b8155d474b7162a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70eadbb19850a9c4ba2a0e7b300dc762
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9a48ee69236a0b49bd29d3a1a8e4519
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e82d99fedd324bb89f27581e90b12ace
|
||||
timeCreated: 1709163553
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e8e769129ee4c1b9adaf2335158ae9b
|
||||
timeCreated: 1709165760
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Schema;
|
||||
using Sirenix.Utilities;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict.Ability;
|
||||
using VOID.Generic.Util;
|
||||
using VOID.ScriptableObjects.Abilities;
|
||||
using Action = Schema.Action;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets ability by selected filters from this unit and save it as blackboard variable")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class GetAbility : Action
|
||||
{
|
||||
private enum SearchType
|
||||
{
|
||||
First,
|
||||
Random,
|
||||
LeastExpensive,
|
||||
MostExpensive,
|
||||
GreatestRange
|
||||
}
|
||||
|
||||
private enum AbilityPurposeType
|
||||
{
|
||||
Any,
|
||||
All
|
||||
}
|
||||
|
||||
[SerializeField] private bool includeBasicAttack;
|
||||
[SerializeField] private BlackboardEntrySelector<float> atLeastRange;
|
||||
[SerializeField] private AbilityPurposeType abilityPurposeType;
|
||||
[SerializeField] private AbilityPurposeFlag abilityPurpose;
|
||||
[SerializeField] private SearchType searchBy;
|
||||
[SerializeField, WriteOnly, Space(15)] private BlackboardEntrySelector<BasicAbility> saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
|
||||
var allAbilities = thisUnit.unitAbilityBook.abilities.ToList();
|
||||
|
||||
// Unit dont have any abilities
|
||||
if (allAbilities.IsNullOrEmpty()) return NodeStatus.Failure;
|
||||
|
||||
if (includeBasicAttack) allAbilities.Add(thisUnit.unitAbilityBook.attackAbility);
|
||||
|
||||
// Filter by range required
|
||||
allAbilities = allAbilities.Where(ability => ability.range >= atLeastRange.value).ToList();
|
||||
|
||||
// Filter by wanted purpose
|
||||
allAbilities = allAbilities.Where(ability =>
|
||||
{
|
||||
switch (abilityPurposeType)
|
||||
{
|
||||
case AbilityPurposeType.All when (ability.abilityPurpose & abilityPurpose) == abilityPurpose:
|
||||
case AbilityPurposeType.Any when (ability.abilityPurpose & abilityPurpose) != AbilityPurposeFlag.None:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
// Getting final ability and saving it
|
||||
saveTo.value = searchBy switch
|
||||
{
|
||||
SearchType.First => allAbilities.FirstOrDefault(),
|
||||
SearchType.Random => RandomUtil.RandomElement(allAbilities),
|
||||
SearchType.LeastExpensive => allAbilities.Aggregate(allAbilities.First(),
|
||||
(a1, a2) => a2.actionPointCost > a1.actionPointCost ? a1 : a2),
|
||||
SearchType.MostExpensive => allAbilities.Aggregate(allAbilities.First(),
|
||||
(a1, a2) => a2.actionPointCost <= a1.actionPointCost ? a1 : a2),
|
||||
SearchType.GreatestRange => allAbilities.Aggregate(allAbilities.First(),
|
||||
(a1, a2) => a2.range <= a1.range ? a1 : a2),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
if (!saveTo.value) return NodeStatus.Failure;
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0912de9aef0840feb6e15e730b800677
|
||||
timeCreated: 1709738283
|
||||
@@ -0,0 +1,23 @@
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.ScriptableObjects.Abilities;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets basic attack ability from this unit and save it as blackboard variable")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class GetAttackAbility : Action
|
||||
{
|
||||
[SerializeField, WriteOnly] public BlackboardEntrySelector<BasicAbility> _saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
|
||||
|
||||
_saveTo.value = thisUnit.unitAbilityBook.attackAbility;
|
||||
if (!_saveTo.value) return NodeStatus.Failure;
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60fa739b5d604f3b9e0a0155377b4692
|
||||
timeCreated: 1710064236
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Schema;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Util;
|
||||
using Action = Schema.Action;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets distance between ThisUnit and target position or unit, minus radius of both of units")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class GetDistanceTo : Action
|
||||
{
|
||||
private enum DistanceFrom
|
||||
{
|
||||
Current,
|
||||
Another
|
||||
}
|
||||
|
||||
private enum DistanceTo
|
||||
{
|
||||
Position,
|
||||
Unit
|
||||
}
|
||||
|
||||
[SerializeField] private DistanceFrom _distanceFrom;
|
||||
[SerializeField, ShowIf(nameof(_distanceFrom), DistanceFrom.Another)] private BlackboardEntrySelector<Vector3> _position;
|
||||
[SerializeField, Space(15)] private DistanceTo _distanceTo;
|
||||
[SerializeField, ShowIf(nameof(_distanceTo), DistanceTo.Position)] private BlackboardEntrySelector<Vector3> _toPosition;
|
||||
[SerializeField, ShowIf(nameof(_distanceTo), DistanceTo.Unit)] private BlackboardEntrySelector<UnitObject> _toUnit;
|
||||
|
||||
[WriteOnly, SerializeField, Space(15)] private BlackboardEntrySelector<float> _saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
var thisUnit = unitAgent.unit;
|
||||
|
||||
_saveTo.value = (_distanceFrom, _distanceTo) switch
|
||||
{
|
||||
(DistanceFrom.Current, DistanceTo.Position) => RangeUtil.GetDistance(thisUnit, _toPosition.value),
|
||||
(DistanceFrom.Current, DistanceTo.Unit) => RangeUtil.GetDistance(thisUnit, _toUnit.value),
|
||||
(DistanceFrom.Another, DistanceTo.Position) => RangeUtil.GetDistance(_toPosition.value, _position.value) - thisUnit.basicData.radius,
|
||||
(DistanceFrom.Another, DistanceTo.Unit) => RangeUtil.GetDistance(_toUnit.value, _position.value) - thisUnit.basicData.radius,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3deac86e81441299d91ee926c0015af
|
||||
timeCreated: 1726666687
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Util;
|
||||
using Action = Schema.Action;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets point where ThisUnit can go with current MovementPoints towards given destination point")]
|
||||
[Category(SchemaCategory.CombatThisUnit)]
|
||||
public class GetReachablePosition : Action
|
||||
{
|
||||
private enum DestinationType
|
||||
{
|
||||
Position,
|
||||
Unit
|
||||
}
|
||||
|
||||
[SerializeField] private DestinationType _destinationType;
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _toUnit;
|
||||
[SerializeField] private BlackboardEntrySelector<Vector3> _toPosition;
|
||||
[SerializeField, WriteOnly, Space(15)] private BlackboardEntrySelector<Vector3> _saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
|
||||
if (!thisUnit.basicState.turnManager) return NodeStatus.Failure;
|
||||
|
||||
// Whole path to destination
|
||||
var path = _destinationType switch
|
||||
{
|
||||
DestinationType.Position => GetPathToPosition(thisUnit),
|
||||
DestinationType.Unit => GetPathToUnit(thisUnit),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
var movePoints = thisUnit.unitData.currentResources.movePoints;
|
||||
|
||||
// Path can't be calculated
|
||||
if (thisUnit.unitNavAgent.GetPath().status is NavMeshPathStatus.PathInvalid) return NodeStatus.Failure;
|
||||
|
||||
// Reachable path (within move points range) and its last possible point
|
||||
NavMeshPathUtil.SplitPath(path.ToArray(), movePoints, out var reachablePath, out _);
|
||||
_saveTo.value = reachablePath.Last();
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
|
||||
private List<Vector3> GetPathToPosition(UnitObject thisUnit)
|
||||
{
|
||||
thisUnit.unitNavAgent.CalculatePathTo(_toPosition.value);
|
||||
return thisUnit.unitNavAgent.GetCorrectedPath();
|
||||
}
|
||||
|
||||
private List<Vector3> GetPathToUnit(UnitObject thisUnit)
|
||||
{
|
||||
thisUnit.unitNavAgent.CalculatePathTo(_toUnit.value);
|
||||
return thisUnit.unitNavAgent.GetCorrectedPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74aab9b029674f94ac8347a3683ca308
|
||||
timeCreated: 1726254338
|
||||
@@ -0,0 +1,19 @@
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets this unit and save it as blackboard variable")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class GetThisUnit : Action
|
||||
{
|
||||
[SerializeField, WriteOnly] public BlackboardEntrySelector<UnitObject> _saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
_saveTo.value = ((UnitObjectSchemaAgent)agent).unit;
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a40ad4564276481f831e44fd55979d34
|
||||
timeCreated: 1709499899
|
||||
@@ -0,0 +1,178 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Util;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Gets unit by selected filters and save it as blackboard variable")]
|
||||
[Category(SchemaCategory.CombatThisUnit)]
|
||||
public class GetUnit : Action
|
||||
{
|
||||
private enum UnitType
|
||||
{
|
||||
Any,
|
||||
Enemy,
|
||||
Neutral,
|
||||
Friendly
|
||||
}
|
||||
|
||||
private enum SearchType
|
||||
{
|
||||
ClosestByDistance,
|
||||
FarthestByDistance,
|
||||
Random,
|
||||
LeastHp,
|
||||
MostHp,
|
||||
ClosestByPath,
|
||||
FarthestByPath
|
||||
}
|
||||
|
||||
[SerializeField] private UnitType _findUnitBy;
|
||||
[SerializeField] private SearchType _searchBy;
|
||||
[SerializeField, Space(15)] private BlackboardEntrySelector<UnitObject> _saveTo;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var thisUnit = ((UnitObjectSchemaAgent)agent).unit;
|
||||
if (!thisUnit.basicState.turnManager) return NodeStatus.Failure;
|
||||
|
||||
// Get all participants in combat except current unit
|
||||
var allUnits = thisUnit.basicState.turnManager.allUnitsInQueue.Where(otherUnit => otherUnit != thisUnit)
|
||||
.ToList();
|
||||
|
||||
// Filter by UnitType
|
||||
if (_findUnitBy is UnitType.Enemy) allUnits = GetEnemy(thisUnit, allUnits);
|
||||
if (_findUnitBy is UnitType.Neutral) allUnits = GetNeutral(thisUnit, allUnits);
|
||||
if (_findUnitBy is UnitType.Friendly) allUnits = GetFriendly(thisUnit, allUnits);
|
||||
|
||||
// Filter by SearchType
|
||||
if (_searchBy is SearchType.ClosestByDistance) _saveTo.value = GetClosestByDistance(thisUnit, allUnits);
|
||||
if (_searchBy is SearchType.FarthestByDistance) _saveTo.value = GetFarthestByDistance(thisUnit, allUnits);
|
||||
if (_searchBy is SearchType.Random) _saveTo.value = RandomUtil.RandomElement(allUnits);
|
||||
if (_searchBy is SearchType.LeastHp) _saveTo.value = GetByLeastHp(allUnits);
|
||||
if (_searchBy is SearchType.MostHp) _saveTo.value = GetByMostHp(allUnits);
|
||||
if (_searchBy is SearchType.ClosestByPath) _saveTo.value = GetClosestByPath(thisUnit, allUnits);
|
||||
if (_searchBy is SearchType.FarthestByPath) _saveTo.value = GetFarthestByPath(thisUnit, allUnits);
|
||||
|
||||
// Could not find anything
|
||||
if (!_saveTo.value) return NodeStatus.Failure;
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
|
||||
private List<UnitObject> GetEnemy(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) < AttitudeType.Neutral)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<UnitObject> GetNeutral(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) == AttitudeType.Neutral)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private List<UnitObject> GetFriendly(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
return units.Where(otherUnit => thisUnit.unitAttitude.GetAttitudeWith(otherUnit) > AttitudeType.Neutral)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private UnitObject GetClosestByDistance(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
var closestDistance = float.MaxValue;
|
||||
var closestUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
var distance = RangeUtil.GetDistance(thisUnit, otherUnit);
|
||||
if (distance >= closestDistance) return;
|
||||
closestDistance = distance;
|
||||
closestUnit = otherUnit;
|
||||
});
|
||||
return closestUnit;
|
||||
}
|
||||
|
||||
private UnitObject GetFarthestByDistance(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
var farthestDistance = float.MinValue;
|
||||
var farthestUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
var distance = RangeUtil.GetDistance(thisUnit, otherUnit);
|
||||
if (distance <= farthestDistance) return;
|
||||
farthestDistance = distance;
|
||||
farthestUnit = otherUnit;
|
||||
});
|
||||
return farthestUnit;
|
||||
}
|
||||
|
||||
private UnitObject GetClosestByPath(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
var closestPathLength = float.MaxValue;
|
||||
var closestUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
thisUnit.unitNavAgent.CalculatePathTo(otherUnit.transform.position);
|
||||
var path = thisUnit.unitNavAgent.GetPath();
|
||||
var pathLength = NavMeshPathUtil.GetPathLength(path);
|
||||
if (path.status is not NavMeshPathStatus.PathInvalid)
|
||||
pathLength += Vector3.Distance(path.corners.Last(), otherUnit.transform.position);
|
||||
if (pathLength >= closestPathLength) return;
|
||||
closestPathLength = pathLength;
|
||||
closestUnit = otherUnit;
|
||||
});
|
||||
return closestUnit;
|
||||
}
|
||||
|
||||
private UnitObject GetFarthestByPath(UnitObject thisUnit, List<UnitObject> units)
|
||||
{
|
||||
var farthestPathLength = float.MinValue;
|
||||
var farthestUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
thisUnit.unitNavAgent.CalculatePathTo(otherUnit.transform.position);
|
||||
var path = thisUnit.unitNavAgent.GetPath();
|
||||
var pathLength = NavMeshPathUtil.GetPathLength(path);
|
||||
if (path.status is not NavMeshPathStatus.PathInvalid)
|
||||
pathLength += Vector3.Distance(path.corners.Last(), otherUnit.transform.position);
|
||||
if (pathLength <= farthestPathLength) return;
|
||||
farthestPathLength = pathLength;
|
||||
farthestUnit = otherUnit;
|
||||
});
|
||||
return farthestUnit;
|
||||
}
|
||||
|
||||
private UnitObject GetByLeastHp(List<UnitObject> units)
|
||||
{
|
||||
var leastHp = float.MinValue;
|
||||
var leastHpUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
var hp = otherUnit.basicData.currentResources.health;
|
||||
if (hp >= leastHp) return;
|
||||
leastHp = hp;
|
||||
leastHpUnit = otherUnit;
|
||||
});
|
||||
return leastHpUnit;
|
||||
}
|
||||
|
||||
private UnitObject GetByMostHp(List<UnitObject> units)
|
||||
{
|
||||
var mostHp = float.MaxValue;
|
||||
var mostHpUnit = (UnitObject)null;
|
||||
units.ForEach(otherUnit =>
|
||||
{
|
||||
var hp = otherUnit.basicData.currentResources.health;
|
||||
if (hp <= mostHp) return;
|
||||
mostHp = hp;
|
||||
mostHpUnit = otherUnit;
|
||||
});
|
||||
return mostHpUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a79026c80b3d42cc922bba86f43a82bb
|
||||
timeCreated: 1709555651
|
||||
@@ -0,0 +1,22 @@
|
||||
using Schema;
|
||||
using VOID.Generic.Objects.Unit.Actions;
|
||||
using VOID.Generic.Objects.Unit.Actions.Exceptions;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Starts turn based fight")]
|
||||
[Category(SchemaCategory.CombatThisUnit)]
|
||||
public class UnitEndTurn : Action
|
||||
{
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
if (!unitAgent.unit || !unitAgent.unit.basicState.turnManager || !unitAgent.unit.unitState.isSelfTurn)
|
||||
return NodeStatus.Failure;
|
||||
|
||||
unitAgent.unit.OrderInstantly(new EndTurnAction(unitAgent.unit));
|
||||
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb47dac215e44eae810d2d4d1537bff5
|
||||
timeCreated: 1709573630
|
||||
@@ -0,0 +1,93 @@
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Objects.Unit.Actions;
|
||||
using VOID.Generic.Objects.Unit.Events;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("This unit will move to given point")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class UnitMoveTo : Action
|
||||
{
|
||||
private class UnitMoveToMemory
|
||||
{
|
||||
public SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory> onMoveEnd;
|
||||
public bool isMoving;
|
||||
public bool isFinished;
|
||||
}
|
||||
|
||||
private enum MoveToType
|
||||
{
|
||||
Position,
|
||||
Unit
|
||||
}
|
||||
|
||||
[SerializeField] private MoveToType _moveTo;
|
||||
[SerializeField] private BlackboardEntrySelector<Transform> _moveToPosition;
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _moveToUnit;
|
||||
[SerializeField, Space(15)] private BlackboardEntrySelector<float> _range;
|
||||
|
||||
public override void OnNodeEnter(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitMoveToMemory)nodeMemory;
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
|
||||
memory.onMoveEnd = new SchemaUnitEventWrapper<MoveEvent, UnitMoveToMemory>(
|
||||
unitAgent.unit.unitEvents.onMoveEnd, OnMoveEnd, unitAgent, memory);
|
||||
memory.onMoveEnd.On();
|
||||
memory.isMoving = false;
|
||||
memory.isFinished = false;
|
||||
}
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitMoveToMemory)nodeMemory;
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
|
||||
// moveTo target not defined
|
||||
if (_moveTo is MoveToType.Unit && !_moveToUnit.value) return NodeStatus.Failure;
|
||||
if (_moveTo is MoveToType.Position && !_moveToPosition.value) return NodeStatus.Failure;
|
||||
|
||||
// unit reached target - end MoveTo with success
|
||||
if (memory.isFinished) return NodeStatus.Success;
|
||||
|
||||
// not started moving yet - start moving for this unit
|
||||
if (!memory.isMoving)
|
||||
{
|
||||
memory.isMoving = true;
|
||||
memory.isFinished = false;
|
||||
unitAgent.unit.OrderStop();
|
||||
if (_moveTo is MoveToType.Position)
|
||||
unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToPosition.value.position, _range.value));
|
||||
if (_moveTo is MoveToType.Unit)
|
||||
unitAgent.unit.Order(new MoveAction(unitAgent.unit, _moveToUnit.value, _range.value));
|
||||
}
|
||||
|
||||
// If somehow this unit don't have anything in current action (something aborted that??)
|
||||
// Then end with failure whole MoveTo
|
||||
if (unitAgent.unit.unitActionQueue.actionCurrent == null)
|
||||
return NodeStatus.Failure;
|
||||
|
||||
// moving
|
||||
return NodeStatus.Running;
|
||||
}
|
||||
|
||||
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitMoveToMemory)nodeMemory;
|
||||
memory.onMoveEnd.Off();
|
||||
}
|
||||
|
||||
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitMoveToMemory)nodeMemory;
|
||||
memory.onMoveEnd.Off();
|
||||
}
|
||||
|
||||
private void OnMoveEnd(MoveEvent moveEvent, UnitMoveToMemory memory, UnitObjectSchemaAgent agent)
|
||||
{
|
||||
memory.isFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 624124ec53d149399992419f68d2a199
|
||||
timeCreated: 1709562199
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit.Actions;
|
||||
using VOID.Generic.Objects.Unit.Events;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("This unit will patrol between given points")]
|
||||
[Category(SchemaCategory.RealtimeThisUnit)]
|
||||
public class UnitPatrol : Action
|
||||
{
|
||||
private class UnitPatrolMemory
|
||||
{
|
||||
public SchemaUnitEventWrapper<MoveEvent, UnitPatrolMemory> onMoveEnd;
|
||||
public int nextStop = - 1;
|
||||
public bool isMoving = false;
|
||||
}
|
||||
|
||||
public override void OnNodeEnter(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitPatrolMemory)nodeMemory;
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
var patrolPoints = unitAgent.unitAI.patrol;
|
||||
var closestDistance = float.MaxValue;
|
||||
|
||||
for (var index = 0; index < patrolPoints.Count; index++)
|
||||
{
|
||||
var distance = Vector3.Distance(unitAgent.unit.transform.position, patrolPoints[index].position);
|
||||
if (distance >= closestDistance) continue;
|
||||
closestDistance = distance;
|
||||
memory.nextStop = index;
|
||||
}
|
||||
|
||||
memory.onMoveEnd = new SchemaUnitEventWrapper<MoveEvent, UnitPatrolMemory>(
|
||||
unitAgent.unit.unitEvents.onMoveEnd, OnMoveEnd, unitAgent, memory);
|
||||
memory.onMoveEnd.On();
|
||||
}
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitPatrolMemory)nodeMemory;
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
|
||||
if (memory.nextStop == - 1) return NodeStatus.Failure;
|
||||
if (memory.isMoving) return NodeStatus.Running;
|
||||
|
||||
// Get next patrol points in order to check
|
||||
var orderedPatrolPoints = new List<Transform>();
|
||||
orderedPatrolPoints.AddRange(unitAgent.unitAI.patrol.Skip(memory.nextStop+1));
|
||||
orderedPatrolPoints.AddRange(unitAgent.unitAI.patrol.Take(memory.nextStop+1));
|
||||
|
||||
// Find next valid patrol point and order move to it
|
||||
foreach (var point in orderedPatrolPoints)
|
||||
{
|
||||
var moveAction = new MoveAction(unitAgent.unit, point.position);
|
||||
if (moveAction.CanDoItBoolean() == false) continue;
|
||||
unitAgent.unit.Order(moveAction);
|
||||
memory.isMoving = true;
|
||||
return NodeStatus.Running;
|
||||
}
|
||||
|
||||
// Next point cant be found - error!
|
||||
return NodeStatus.Failure;
|
||||
}
|
||||
|
||||
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitPatrolMemory)nodeMemory;
|
||||
memory.onMoveEnd.Off();
|
||||
memory.isMoving = false;
|
||||
}
|
||||
|
||||
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitPatrolMemory)nodeMemory;
|
||||
memory.onMoveEnd.Off();
|
||||
memory.isMoving = false;
|
||||
}
|
||||
|
||||
private void OnMoveEnd(MoveEvent moveEvent, UnitPatrolMemory memory, UnitObjectSchemaAgent agent)
|
||||
{
|
||||
memory.nextStop = (memory.nextStop + 1) % agent.unitAI.patrol.Count;
|
||||
memory.isMoving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a11564bc03bb4a52a083e28d35d798cf
|
||||
timeCreated: 1709223806
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using Schema;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Turn;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Starts turn based fight")]
|
||||
[Category(SchemaCategory.RealtimeThisUnit)]
|
||||
public class UnitStartFight : Action
|
||||
{
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
var hostileUnits = unitAgent.unit.unitSenses.GetUnitsInSightByAttitude(AttitudeType.Hostile);
|
||||
if (hostileUnits.Count == 0) return NodeStatus.Failure;
|
||||
var units = new List<UnitObject>();
|
||||
units.Add(unitAgent.unit);
|
||||
units.AddRange(hostileUnits);
|
||||
TurnManager.BuildGameObject(units);
|
||||
return NodeStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62ecce8528974c928665fff4143169dd
|
||||
timeCreated: 1709379357
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Objects.Unit.Actions;
|
||||
using VOID.Generic.Objects.Unit.Events;
|
||||
using VOID.ScriptableObjects.Abilities;
|
||||
using Action = Schema.Action;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Actions
|
||||
{
|
||||
[Description("Order this unit to use selected ability")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class UnitUseAbility : Action
|
||||
{
|
||||
private class UnitUseAbilityMemory
|
||||
{
|
||||
public SchemaUnitEventWrapper<AnimationGenericEvent, UnitUseAbilityMemory> onCastAnimationEnd;
|
||||
public bool isRunning;
|
||||
public bool isFinished;
|
||||
}
|
||||
|
||||
private enum TargetType
|
||||
{
|
||||
Position,
|
||||
Unit
|
||||
}
|
||||
|
||||
[SerializeField] private BlackboardEntrySelector<BasicAbility> _ability;
|
||||
[SerializeField, Space(15)] private TargetType _targetType;
|
||||
[SerializeField] private BlackboardEntrySelector<Transform> _position;
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
|
||||
|
||||
public override NodeStatus Tick(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitUseAbilityMemory)nodeMemory;
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
var thisUnit = unitAgent.unit;
|
||||
|
||||
// Ability not selected
|
||||
if (!_ability.value) return NodeStatus.Failure;
|
||||
|
||||
// Whenever is during casting or already done that
|
||||
if (memory.isFinished) return NodeStatus.Success;
|
||||
if (memory.isRunning) return NodeStatus.Running;
|
||||
|
||||
// Targets not selected
|
||||
if (_targetType == TargetType.Position && !_position.value) return NodeStatus.Failure;
|
||||
if (_targetType == TargetType.Unit && !_unit.value) return NodeStatus.Failure;
|
||||
|
||||
AbilityAction abilityAction;
|
||||
if (_targetType is TargetType.Position)
|
||||
{
|
||||
// TODO: for now if ability needs multiple targets all will be identical
|
||||
var targets = Enumerable.Repeat(_position.value.position, _ability.value.castUsages).ToArray();
|
||||
abilityAction = new AbilityAction(thisUnit, _ability.value, targets);
|
||||
}
|
||||
else if (_targetType is TargetType.Unit)
|
||||
{
|
||||
// TODO: for now if ability needs multiple targets all will be identical
|
||||
var targets = Enumerable.Repeat(_unit.value, _ability.value.castUsages).ToArray();
|
||||
abilityAction = new AbilityAction(thisUnit, _ability.value, targets);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Check if that action can be done
|
||||
if (!abilityAction.CanDoItBoolean()) return NodeStatus.Failure;
|
||||
|
||||
// Listen to end of abilisty casting animation
|
||||
memory.onCastAnimationEnd?.Off();
|
||||
memory.onCastAnimationEnd = new SchemaUnitEventWrapper<AnimationGenericEvent, UnitUseAbilityMemory>(
|
||||
thisUnit.unitEvents.onAnimation[AnimationType.Ability].onEnd, OnAbilityCastEnd, unitAgent, memory);
|
||||
memory.onCastAnimationEnd.On();
|
||||
|
||||
// Everything seems ok - order unit to do that action
|
||||
thisUnit.OrderStop();
|
||||
thisUnit.Order(abilityAction);
|
||||
memory.isRunning = true;
|
||||
|
||||
return NodeStatus.Running;
|
||||
}
|
||||
|
||||
public override void OnNodeExit(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitUseAbilityMemory)nodeMemory;
|
||||
memory.onCastAnimationEnd?.Off();
|
||||
memory.isFinished = false;
|
||||
memory.isRunning = false;
|
||||
}
|
||||
|
||||
public override void OnNodeAbort(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var memory = (UnitUseAbilityMemory)nodeMemory;
|
||||
memory.onCastAnimationEnd?.Off();
|
||||
memory.isFinished = false;
|
||||
memory.isRunning = false;
|
||||
}
|
||||
|
||||
private void OnAbilityCastEnd(AnimationGenericEvent abilityCastEvent, UnitUseAbilityMemory memory,
|
||||
UnitObjectSchemaAgent agent)
|
||||
{
|
||||
memory.isFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45d528d62fdb41c7b539945d8d9f39be
|
||||
timeCreated: 1709821286
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a75df9d1888e48babf6ec8f326d7d85a
|
||||
timeCreated: 1709166099
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using Sirenix.Utilities;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
using VOID.Generic.Util;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.AnyUnit)]
|
||||
public class CanUnitMoveTo : Conditional
|
||||
{
|
||||
private enum MoveToType
|
||||
{
|
||||
Position,
|
||||
Unit
|
||||
}
|
||||
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
|
||||
[SerializeField, Space(15)] private MoveToType _moveTo;
|
||||
[SerializeField] private BlackboardEntrySelector<Transform> _moveToPosition;
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _moveToUnit;
|
||||
[SerializeField, Space(15)] private BlackboardEntrySelector<float> _range;
|
||||
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
var unitAgent = (UnitObjectSchemaAgent)agent;
|
||||
|
||||
switch (_moveTo)
|
||||
{
|
||||
case MoveToType.Position:
|
||||
_unit.value.unitNavAgent.CalculatePathTo(_moveToPosition.value.position,
|
||||
_range.value + unitAgent.unit.basicData.radius);
|
||||
break;
|
||||
case MoveToType.Unit:
|
||||
_unit.value.unitNavAgent.CalculatePathTo(_moveToUnit.value.transform.position,
|
||||
_range.value + unitAgent.unit.basicData.radius + _moveToUnit.value.basicData.radius);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var path = _unit.value.unitNavAgent.GetCorrectedPath();
|
||||
|
||||
// If somehow path isnt calculated - cant move
|
||||
if (path.IsNullOrEmpty()) return false;
|
||||
|
||||
// Out of combat - costless move
|
||||
if (!_unit.value.basicState.turnManager) return true;
|
||||
|
||||
// Calculate move cost when in turn combat
|
||||
var cost = NavMeshPathUtil.CalculatePathCost(path);
|
||||
return _unit.value.unitData.currentResources.movePoints >= cost;
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append($"If <color=red>{_unit.name}</color> can move to ");
|
||||
if (_moveTo is MoveToType.Position) sb.Append($"<color=red>{_moveToPosition.name}</color>");
|
||||
if (_moveTo is MoveToType.Unit) sb.Append($"<color=red>{_moveToUnit.name}</color>");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3b68fb79d3b4f19bee514ad03229447
|
||||
timeCreated: 1709732599
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.AnyUnit)]
|
||||
public class IsBasicResource : Conditional
|
||||
{
|
||||
private enum ComparisonType
|
||||
{
|
||||
Greater,
|
||||
Less
|
||||
}
|
||||
|
||||
private enum ValueType
|
||||
{
|
||||
Value,
|
||||
Percent
|
||||
}
|
||||
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
|
||||
[SerializeField, Space(15)] private BasicResourceType _resourceType;
|
||||
[SerializeField] private ComparisonType _comparisonType;
|
||||
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _value = new(1);
|
||||
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Percent)] private BlackboardEntrySelector<int> _percent = new(50);
|
||||
[SerializeField] private ValueType _compareBy;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
_percent.inspectorValue = Mathf.Min(Mathf.Max(_percent.inspectorValue, 0), 100);
|
||||
_value.inspectorValue = Mathf.Max(_value.inspectorValue, 0);
|
||||
}
|
||||
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return _compareBy switch
|
||||
{
|
||||
ValueType.Percent => CompareByPercent(),
|
||||
ValueType.Value => CompareByValue(),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private bool CompareByValue()
|
||||
{
|
||||
var currentResource = _unit.value.basicData.currentResources.Get(_resourceType);
|
||||
|
||||
return _comparisonType switch
|
||||
{
|
||||
ComparisonType.Greater => currentResource > _value.value,
|
||||
ComparisonType.Less => currentResource < _value.value,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private bool CompareByPercent()
|
||||
{
|
||||
var currentResource = _unit.value.basicData.currentResources.Get(_resourceType);
|
||||
var maxResource = _unit.value.basicData.maxResources.Get(_resourceType);
|
||||
var currentPercent = currentResource / maxResource * 100;
|
||||
|
||||
return _comparisonType switch
|
||||
{
|
||||
ComparisonType.Greater => currentPercent > _percent.value,
|
||||
ComparisonType.Less => currentPercent < _percent.value,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append(
|
||||
$"If <color=red>{_unit.name}</color> {nameof(UnitObjectData)}'s <color=red>{_resourceType}</color>");
|
||||
sb.Append(_comparisonType == ComparisonType.Greater ? " > " : " < ");
|
||||
if (_compareBy is ValueType.Percent) sb.Append($"<color=red>{_percent.name}</color>%");
|
||||
if (_compareBy is ValueType.Value) sb.Append($"<color=red>{_value.name}</color>");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1245ec365cb943c6acc3d9e85330d07a
|
||||
timeCreated: 1709501957
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class IsPatrolDefined : Conditional
|
||||
{
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return (agent as UnitObjectSchemaAgent)?.unitAI.patrol.Count > 1;
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append($"<color=red>{nameof(UnitObjectSenses)}</color> has at least two <color=red>patrol</color> points");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74564f3f9d4b467bb3e954c5a84cf780
|
||||
timeCreated: 1709383094
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.AnyUnit)]
|
||||
public class IsUnitDead : Conditional
|
||||
{
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
|
||||
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return _unit.value && _unit.value.basicState.isDead;
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append($"If <color=red>{_unit.name}</color> is dead");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba8914a1869a4e06aebdeebf2ec743cd
|
||||
timeCreated: 1710974135
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class IsUnitInSight : Conditional
|
||||
{
|
||||
[SerializeField] private AttitudeType _attitudeType;
|
||||
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return (agent as UnitObjectSchemaAgent)?.unit.unitSenses.GetUnitsInSightByAttitude(_attitudeType).Count > 0;
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append(
|
||||
$"<color=red>{nameof(UnitObjectSenses)}</color> has at least one <color=red>{_attitudeType}</color> in range");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6eed700ee23b489189517c4ae32e687a
|
||||
timeCreated: 1709382718
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.ThisUnit)]
|
||||
public class IsUnitObjectSchema : Conditional
|
||||
{
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return agent is UnitObjectSchemaAgent unitAgent && unitAgent.unit;
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append($"If this is <color=red>{nameof(UnitObject)}</color>'s schema");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b727768fcf6433687f118b54fe16e53
|
||||
timeCreated: 1709203631
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Schema;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit;
|
||||
|
||||
namespace VOID.Generic.AI.Schema.Conditionals
|
||||
{
|
||||
[DarkIcon("Conditionals/d_IsNull")]
|
||||
[LightIcon("Conditionals/IsNull")]
|
||||
[Category(SchemaCategory.AnyUnit)]
|
||||
public class IsUnitResource : Conditional
|
||||
{
|
||||
private enum ComparisonType
|
||||
{
|
||||
Greater,
|
||||
Less
|
||||
}
|
||||
|
||||
private enum ValueType
|
||||
{
|
||||
Value,
|
||||
Percent
|
||||
}
|
||||
|
||||
[SerializeField] private BlackboardEntrySelector<UnitObject> _unit;
|
||||
[SerializeField, Space(15)] private UnitResourceType _resourceType;
|
||||
[SerializeField] private ComparisonType _comparisonType;
|
||||
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _value = new(1);
|
||||
[SerializeField, ShowIf(nameof(_compareBy), ValueType.Value)] private BlackboardEntrySelector<int> _percent = new(50);
|
||||
[SerializeField] private ValueType _compareBy;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
_percent.inspectorValue = Mathf.Min(Mathf.Max(_percent.inspectorValue, 0), 100);
|
||||
_value.inspectorValue = Mathf.Max(_value.inspectorValue, 0);
|
||||
}
|
||||
|
||||
public override bool Evaluate(object nodeMemory, SchemaAgent agent)
|
||||
{
|
||||
return _compareBy switch
|
||||
{
|
||||
ValueType.Percent => CompareByPercent(),
|
||||
ValueType.Value => CompareByValue(),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private bool CompareByValue()
|
||||
{
|
||||
var currentResource = _unit.value.unitData.currentResources.Get(_resourceType);
|
||||
|
||||
return _comparisonType switch
|
||||
{
|
||||
ComparisonType.Greater => currentResource > _value.value,
|
||||
ComparisonType.Less => currentResource < _value.value,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private bool CompareByPercent()
|
||||
{
|
||||
var currentResource = _unit.value.unitData.currentResources.Get(_resourceType);
|
||||
var maxResource = _unit.value.unitData.maxResources.Get(_resourceType);
|
||||
var currentPercent = currentResource / maxResource * 100;
|
||||
|
||||
return _comparisonType switch
|
||||
{
|
||||
ComparisonType.Greater => currentPercent > _percent.value,
|
||||
ComparisonType.Less => currentPercent < _percent.value,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public override GUIContent GetConditionalContent()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (invert) sb.Append("<color=red>NOT</color> ");
|
||||
sb.Append(
|
||||
$"If <color=red>{_unit.name}</color> {nameof(UnitObjectData)}'s <color=red>{_resourceType}</color>");
|
||||
sb.Append(_comparisonType == ComparisonType.Greater ? " > " : " < ");
|
||||
if (_compareBy is ValueType.Percent) sb.Append($"<color=red>{_percent.name}</color>%");
|
||||
if (_compareBy is ValueType.Value) sb.Append($"<color=red>{_value.name}</color>");
|
||||
return new GUIContent(sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user