This commit is contained in:
2026-07-09 21:33:33 +02:00
commit 84a2a365f3
2364 changed files with 950134 additions and 0 deletions
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 92d4ae60438d4ddc92655e075655c935
timeCreated: 1719507871
@@ -0,0 +1,114 @@
using Sirenix.OdinInspector;
using UnityEngine;
namespace VOID.Generic.Projectile.Drawers
{
public class BeamProjectileDrawer : MonoBehaviour
{
[Title("Positions")]
[SerializeField] private bool autoPositions;
[HideIf(nameof(autoPositions))] [SerializeField] [Required] private Transform from;
[HideIf(nameof(autoPositions))] [SerializeField] [Required] private Transform to;
[Title("Prefabs")]
[InfoBox("Those prefabs are from addon MagicArsenal located in <b>Assets/_ADDONS/MagicArsenal/Effects/Prefabs/Beams/Setup</b>")]
[SerializeField] [Required] private GameObject beamLineRendererPrefab;
[SerializeField] [Required] private GameObject beamStartPrefab;
[SerializeField] [Required] private GameObject beamEndPrefab;
[Title("Beam Options")]
[SerializeField] private float textureScrollSpeed = 5f;
[SerializeField] private float textureLengthScale = 5f;
[Title("Width Pulse Options")]
[SerializeField] private float startScale = 1.0f;
[SerializeField] private float endScale = 1.0f;
[SerializeField] private float pulseSpeed = 1.0f;
private GameObject _beamStart;
private GameObject _beamEnd;
private GameObject _beam;
private LineRenderer _line;
private float _lerpValue;
private bool _pulseExpanding;
public void Awake()
{
if (autoPositions)
{
GetComponent<ProjectileObject>().onStart.AddListener(SetPositions);
}
}
private void Start()
{
SpawnBeam();
}
private void SetPositions(ProjectileObject projectile)
{
from = projectile.caster.unitData.castPosition;
to = projectile.gameObject.transform;
}
private void FixedUpdate()
{
if (_beam)
{
var fromPosition = from.position;
var toPosition = to.position;
_line.SetPosition(0, fromPosition);
_line.SetPosition(1, toPosition);
_beamStart.transform.position = fromPosition;
_beamStart.transform.LookAt(toPosition);
_beamEnd.transform.position = toPosition;
_beamEnd.transform.LookAt(fromPosition);
var distance = Vector3.Distance(fromPosition, toPosition);
//This sets the scale of the texture so it doesn't look stretched
_line.material.mainTextureScale = new Vector2(distance / textureLengthScale, 1);
//This scrolls the texture along the beam if not set to 0
_line.material.mainTextureOffset -= new Vector2(Time.deltaTime * textureScrollSpeed, 0);
}
_lerpValue += (_pulseExpanding ? 1 : -1) * Time.deltaTime * pulseSpeed;
if (_lerpValue >= 1.0f)
{
_pulseExpanding = false;
_lerpValue = 1.0f;
}
else if (_lerpValue <= 0.0f)
{
_pulseExpanding = true;
_lerpValue = 0.0f;
}
var currentWidth = Mathf.Lerp(startScale, endScale, Mathf.Sin(_lerpValue * Mathf.PI));
_line.startWidth = currentWidth;
_line.endWidth = currentWidth;
}
private void SpawnBeam()
{
if (!beamLineRendererPrefab) return;
_beam = Instantiate(beamLineRendererPrefab);
_beam.transform.position = transform.position;
_beam.transform.parent = transform;
_beam.transform.rotation = transform.rotation;
_line = _beam.GetComponent<LineRenderer>();
_line.useWorldSpace = true;
_line.positionCount = 2;
_beamStart = beamStartPrefab ? Instantiate(beamStartPrefab, _beam.transform) : null;
_beamEnd = beamEndPrefab ? Instantiate(beamEndPrefab, _beam.transform) : null;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e925d1593dd43368f3237aa42ca8315
timeCreated: 1719507917
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 95c27c54fd7e439fbc57d8a89b3eda77
timeCreated: 1694376610
@@ -0,0 +1,10 @@
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.HitValidator
{
public interface IProjectileHitValidator
{
public bool IsValid(UnitObject caster, AbstractObject target);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 923e7c5bc9bd414297e822c7c46ad8b0
@@ -0,0 +1,16 @@
using Sirenix.OdinInspector;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Item;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.HitValidator
{
[TypeRegistryItem("Item")]
public class ProjectileHitItem : IProjectileHitValidator
{
public bool IsValid(UnitObject caster, AbstractObject target)
{
return target is ItemObject;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 164b8f6c007ed544da396cd6a9874d65
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.HitValidator
{
[TypeRegistryItem("Other")]
public class ProjectileHitOther : IProjectileHitValidator
{
public bool IsValid(UnitObject caster, AbstractObject target)
{
return caster != target;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b11ba2fe64fb42e794811cf45a8e03ec
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.HitValidator
{
[TypeRegistryItem("Self")]
public class ProjectileHitSelf : IProjectileHitValidator
{
public bool IsValid(UnitObject caster, AbstractObject target)
{
return caster == target;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aa232c3837fa4f23afe8df94a0e3db7b
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.HitValidator
{
[TypeRegistryItem("Unit")]
public class ProjectileHitUnit : IProjectileHitValidator
{
[SerializeField] private List<AttitudeType> _attitudes = new();
public bool IsValid(UnitObject caster, AbstractObject target)
{
if (target is not UnitObject targetUnit) return false;
if (_attitudes.IsNullOrEmpty()) return true;
if (_attitudes.Contains(caster.unitAttitude.GetAttitudeWith(targetUnit))) return true;
return false;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b1c051c72ef24497a5206391bd86e57c
@@ -0,0 +1,15 @@
using System;
using UnityEngine;
namespace VOID.Generic.Projectile
{
[DefaultExecutionOrder(1)]
public class ProjectileAoe : MonoBehaviour
{
public event Action<Collider> OnAoeEnter;
public event Action<Collider> OnAoeExit;
private void OnTriggerEnter(Collider other) => OnAoeEnter?.Invoke(other);
private void OnTriggerExit(Collider other) => OnAoeExit?.Invoke(other);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 908f4254ee0809d4d81ac1c1cd417188
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector;
namespace VOID.Generic.Projectile
{
[InfoBox("What can stop this projectile:\n" +
"<u><b>" + nameof(StaticOrDynamicCollision) + "</b></u> any collider\n" +
"<u><b>" + nameof(StaticCollision) + "</b></u> only static colliders\n" +
"<u><b>" + nameof(NoCollision) + "</b></u> nothing can stop this projectile")]
public enum ProjectileCollisionType
{
StaticOrDynamicCollision,
StaticCollision,
NoCollision,
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 070cbc890f4b4c788eb4ca3a289fbc72
timeCreated: 1675895859
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector;
namespace VOID.Generic.Projectile
{
[InfoBox("When projectile can do *OnHit* objects:\n" +
"<u><b>" + nameof(Always) + "</b></u> when they touch projectile's collider\n" +
"<u><b>" + nameof(OnEnd) + "</b></u> when projectile reach step's end\n" +
"<u><b>" + nameof(Never) + "</b></u> projectile on current step won't trigger OnHit")]
public enum ProjectileHitType
{
Always,
OnEnd,
Never,
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 191fa44350354b9dafabf09f8a96d639
timeCreated: 1675895939
@@ -0,0 +1,267 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Sirenix.Utilities;
using UnityEngine;
using UnityEngine.Events;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Trajectory;
namespace VOID.Generic.Projectile
{
/// <summary>
/// Moves projectile on defined <see cref="ProjectileStep"/>
/// </summary>
[DefaultExecutionOrder(-1)]
public class ProjectileObject : MonoBehaviour
{
// Projectile Configuration
public int index { private set; get; }
public UnitObject caster { private set; get; }
public ProjectileStep[] steps { private set; get; }
// Events
public UnityEvent<ProjectileObject> onStart = new();
public UnityEvent<ProjectileObject, AbstractObject> onHit = new();
public UnityEvent<ProjectileObject> onNextStep = new();
public UnityEvent<ProjectileObject> onEnd = new();
// Step info
private ProjectileStep _currentStep;
private int _currentStepIndex = -1;
private float _currentStepDistance;
private AbstractTrajectory _currentTrajectory;
private Vector3 _startPosition;
private Vector3 _targetPosition;
private Vector3 _lastHitPosition;
// Projectile and its AOE game objects
private GameObject _gameObject;
private GameObject _aoeGameObject;
// Storing collisions
private readonly Dictionary<AbstractObject, int> _aoeCollisions = new();
private void Awake()
{
gameObject.SetActive(false);
}
private void FixedUpdate()
{
if (MoveProjectile() || CheckCollision()) StartCoroutine(NextStep());
}
/// <summary>
/// Moves projectile by one step in trajectory.
/// </summary>
/// <returns>TRUE if current step reached end</returns>
private bool MoveProjectile()
{
_currentStepDistance += Time.fixedDeltaTime * _currentStep.speed;
var newProgress = _currentTrajectory.GetProgressForDistance(_currentStepDistance);
var newPosition = _currentTrajectory.GetPositionByProgress(newProgress);
// Update rotation
_gameObject.transform.rotation = Quaternion.LookRotation(transform.position - newPosition);
// Update position
_aoeGameObject.transform.position = transform.position = newPosition;
return Mathf.Approximately(newProgress, 1f);
}
/// <summary>
/// Checks if projectile should end early because it hit something.
/// </summary>
/// <returns>TRUE if collision ends this projectile step</returns>
private bool CheckCollision()
{
int layerMask;
switch (_currentStep.collisionType)
{
case ProjectileCollisionType.StaticOrDynamicCollision:
layerMask = LayerManager.Static | LayerManager.StaticHideable | LayerManager.Dynamic;
break;
case ProjectileCollisionType.StaticCollision:
layerMask = LayerManager.Static | LayerManager.StaticHideable;
break;
default:
return false;
}
// Just to be sure - ignore caster :D
var colliders = new Collider[2];
Physics.OverlapSphereNonAlloc(transform.position, _currentStep.radius, colliders, layerMask);
return colliders.Any(collider => collider && collider.GetComponentInParent<UnitObject>() != caster);
}
/// <summary>
/// Starts next step of projectile. If no more steps available then whole projectile is destroyed.
/// </summary>
private IEnumerator NextStep()
{
// IMPORTANT
// 1. Collisions calculate after fixed update, we need to wait 1 (or 2?) fixed updates
// to be sure that we dont ignore collision that will occour later this frame in projectile's AOE
// 2. Also we want to stop FixedUpdate from running when this happens otherwise this part will be executed twice
enabled = false;
yield return new WaitForFixedUpdate();
enabled = true;
if (_currentStep.hitType == ProjectileHitType.OnEnd)
_aoeCollisions.Keys.ForEach(hitGameObject => onHit.Invoke(this, hitGameObject));
if (_currentStepIndex >= steps.Length - 1)
{
EndProjectile();
yield break;
}
_lastHitPosition = gameObject.transform.position;
onNextStep.Invoke(this);
PrepareNextStep();
}
/// <summary>
/// 1. Clear temporary data<br/>
/// 2. Initialize next Trajectory<br/>
/// 3. Initialize next Area Of Effect
/// </summary>
private void PrepareNextStep()
{
// Clear temp data about previous step
_currentStepDistance = 0f;
_currentStep = steps[++_currentStepIndex];
_aoeCollisions.Clear();
_currentTrajectory = _currentStep.trajectory.Clone();
// Update AOE GameObject's rotation and collider by data in AreaOfEffect
// This process can be done once when starting new step, because AOE dont change during flight
if (_aoeGameObject.TryGetComponent(out Collider aoeCollider)) Destroy(aoeCollider);
_currentStep.areaOfEffect.AddCollider(_aoeGameObject);
_aoeGameObject.transform.rotation = _currentStep.areaOfEffect.GetRotation(_startPosition, _targetPosition);
// Prepare trajectory
_currentTrajectory.Init(FindStartPositionForStep(), _targetPosition);
}
/// <summary>
/// Returns start position for current step by defined <see cref="ProjectileStepStartFrom"/>.
/// </summary>
private Vector3 FindStartPositionForStep()
{
return _currentStep.startFrom switch
{
ProjectileStepStartFrom.FromStart => _startPosition,
ProjectileStepStartFrom.FromCaster => caster.transform.position,
ProjectileStepStartFrom.FromHit => _lastHitPosition == default ? _lastHitPosition : _startPosition,
_ => throw new ArgumentOutOfRangeException(
nameof(_currentStep.startFrom), _currentStep.startFrom, null)
};
}
/// <summary>
/// Initializes and starts whole projectile.
/// </summary>
/// <param name="index">Index of this projectile from source ability</param>
/// <param name="caster">UnitObject that fired this projectile</param>
/// <param name="startPosition">Projectile starting position</param>
/// <param name="targetPosition">Projectile target position</param>
/// <param name="steps">Definition HOW projectile should move and hit</param>
public void StartProjectile(
int index,
UnitObject caster,
Vector3 startPosition,
Vector3 targetPosition,
ProjectileStep[] steps
)
{
this.index = index;
this.caster = caster;
this.steps = steps;
_startPosition = startPosition;
_targetPosition = targetPosition;
// Projectile GameObject
_gameObject = gameObject;
_gameObject.layer = LayerManager.TriggerIndex;
_gameObject.transform.position = _startPosition;
_gameObject.SetActive(true);
// Projectile AOE GameObject
_aoeGameObject = new GameObject("Projectile AOE collider");
_aoeGameObject.layer = LayerManager.TriggerIndex;
_aoeGameObject.transform.position = gameObject.transform.position;
// Projectile AOE controlling component
var projectileAoe = _aoeGameObject.AddComponent<ProjectileAoe>();
projectileAoe.OnAoeEnter += OnAoeEnter;
projectileAoe.OnAoeExit += OnAoeExit;
onStart.Invoke(this);
PrepareNextStep();
}
/// <summary>
/// Ends and destroy projectile.
/// </summary>
private void EndProjectile()
{
onEnd.Invoke(this);
// Destroying only projectile's AOE and script, not whole projectile yet!
Destroy(this);
Destroy(_aoeGameObject);
// Wait a little bit for other things like VFX to finish then we can safely destroy whole projectile
Destroy(_gameObject, 3);
}
/// <summary>
/// Checking if given target should be hit and executed OnHit.
/// </summary>
private bool IsValidHit(UnitObject caster, AbstractObject target)
{
return _currentStep.validHits.Count == 0 ||
_currentStep.validHits.Any(validator => validator.IsValid(caster, target));
}
/// <summary>
/// Collision enter detected by projectile AOE.
/// </summary>
private void OnAoeEnter(Collider other)
{
if (!LayerManager.CodeInMask(other.gameObject.layer, LayerManager.Dynamic)) return;
var hitObject = other.GetComponentInParent<AbstractObject>();
if (!hitObject) return;
if (!IsValidHit(caster, hitObject)) return;
_aoeCollisions.TryAdd(hitObject, 0);
_aoeCollisions[hitObject]++;
if (_currentStep.hitType == ProjectileHitType.Always) onHit.Invoke(this, hitObject);
}
/// <summary>
/// Collision exit detected by projectile AOE.
/// </summary>
private void OnAoeExit(Collider other)
{
if (!LayerManager.CodeInMask(other.gameObject.layer, LayerManager.Dynamic)) return;
var hitObject = other.GetComponentInParent<AbstractObject>();
if (!hitObject) return;
if (!_aoeCollisions.ContainsKey(hitObject)) return;
_aoeCollisions[hitObject]--;
if (_aoeCollisions[hitObject] == 0) _aoeCollisions.Remove(hitObject);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60bc05bd3f5944328cf0613fee18cd40
timeCreated: 1668807593
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.AreaOfEffect;
using VOID.Generic.Projectile.HitValidator;
using VOID.Generic.Projectile.TargetValidator;
using VOID.Generic.Trajectory;
using VOID.ScriptableObjects.Abilities;
namespace VOID.Generic.Projectile
{
/// <summary>
/// Defined in <see cref="BasicAbility"/> and used by <see cref="ProjectileObject"/>.
/// Defines how projectile will move and hit.
/// </summary>
[Serializable]
public class ProjectileStep
{
// Projectile own HitBox and speed
[MinValue(.1f)] public float radius = .1f;
[OnValueChanged(nameof(OnValidate)), ShowIf(nameof(ShowSpeed)), MinValue(1f), SuffixLabel("m/s")] public float speed = 10f;
// Variables which enhance projectile movement and trajectory
[OnValueChanged(nameof(OnValidate)), ShowIf(nameof(ShowHitType)), Required] public ProjectileHitType hitType;
[OnValueChanged(nameof(OnValidate)), Required] public ProjectileCollisionType collisionType;
[Required] public ProjectileStepStartFrom startFrom;
// Validating if can be targeted or hit
[InfoBox("Validates if target can be selected")]
[SerializeReference, HorizontalGroup("validating")] public List<IProjectileTargetValidator> validTargets = new();
[InfoBox("Validates if object touched by AOE can be hit")]
[SerializeReference, HorizontalGroup("validating")] public List<IProjectileHitValidator> validHits = new();
// Generic definition of TRAJECTORY and AOE
[SerializeReference, Required] public AbstractTrajectory trajectory;
[SerializeReference, Required] public AbstractAreaOfEffect areaOfEffect;
private void OnValidate()
{
if (!ShowSpeed()) speed = 10f;
if (!ShowHitType()) hitType = ProjectileHitType.Never;
}
private bool ShowSpeed() => trajectory is not InstantTrajectory;
private bool ShowHitType() => areaOfEffect is not NoneAreaOfEffect;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49b63bd283fa450faa65160fdfc490d3
timeCreated: 1675895813
@@ -0,0 +1,15 @@
using Sirenix.OdinInspector;
namespace VOID.Generic.Projectile
{
[InfoBox("Starting point for this projectile's step:\n" +
"<u><b>" + nameof(FromStart) + "</b></u> from casting position\n" +
"<u><b>" + nameof(FromCaster) + "</b></u> from caster's position\n" +
"<u><b>" + nameof(FromHit) + "</b></u> from current projectile's position")]
public enum ProjectileStepStartFrom
{
FromStart,
FromCaster,
FromHit,
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8fcb71fa9d824e3197961d71956e7115
timeCreated: 1694200927
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40e53ece385640aeafea80551c238724
timeCreated: 1731080883
@@ -0,0 +1,12 @@
using JetBrains.Annotations;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
public interface IProjectileTargetValidator
{
public bool IsValid(UnitObject caster, Vector3 targetPosition, [CanBeNull] AbstractObject targetObject);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f5995c2ee1ec41f0b43069e5d8fd5bb5
timeCreated: 1731080877
@@ -0,0 +1,17 @@
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Item;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
[TypeRegistryItem("Item")]
public class ProjectileTargetItem : IProjectileTargetValidator
{
public bool IsValid(UnitObject caster, Vector3 targetPosition, AbstractObject targetObject)
{
return targetObject is ItemObject;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50f8b528d89f4254b041adf5f3400e13
timeCreated: 1731099384
@@ -0,0 +1,16 @@
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
[TypeRegistryItem("Other")]
public class ProjectileTargetOther : IProjectileTargetValidator
{
public bool IsValid(UnitObject caster, Vector3 targetPosition, AbstractObject targetObject)
{
return caster != targetObject;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa20fa37f9ea4484ac5181068fc23e03
timeCreated: 1731089158
@@ -0,0 +1,16 @@
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
[TypeRegistryItem("Self")]
public class ProjectileTargetSelf : IProjectileTargetValidator
{
public bool IsValid(UnitObject caster, Vector3 targetPosition, AbstractObject targetObject)
{
return caster == targetObject;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2aac834351ab47d793aa3647a5b3e4ee
timeCreated: 1731099384
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
[TypeRegistryItem("Unit")]
public class ProjectileTargetUnit : IProjectileTargetValidator
{
[SerializeField] private List<AttitudeType> _attitudes = new();
public bool IsValid(UnitObject caster, Vector3 targetPosition, AbstractObject targetObject)
{
if (targetObject is not UnitObject targetUnit) return false;
if (_attitudes.IsNullOrEmpty()) return true;
if (_attitudes.Contains(caster.unitAttitude.GetAttitudeWith(targetUnit))) return true;
return false;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 443176e8033443c6b192c4d873ed81b1
timeCreated: 1731099384
@@ -0,0 +1,16 @@
using Sirenix.OdinInspector;
using UnityEngine;
using VOID.Generic.Objects.Abstract;
using VOID.Generic.Objects.Unit;
namespace VOID.Generic.Projectile.TargetValidator
{
[TypeRegistryItem("Walkable")]
public class ProjectileTargetWalkable : IProjectileTargetValidator
{
public bool IsValid(UnitObject caster, Vector3 targetPosition, AbstractObject targetObject)
{
return caster.unitNavAgent.IsValidGround(targetPosition);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 208d383dab1d4481a6a5e487d9aa3c13
timeCreated: 1731099524