// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using UltimateXR.Core.Caching;
using UltimateXR.Core.Components;
using UnityEngine;
namespace UltimateXR.Mechanics.Weapons
{
///
/// Component that has the ability to fire shots.
///
public class UxrProjectileSource : UxrComponent, IUxrPrecacheable
{
#region Inspector Properties/Serialized Fields
[SerializeField] private Animator _weaponAnimator;
[SerializeField] private List _shotTypes;
#endregion
#region Public Types & Data
///
/// The different shots that can be fired using the component.
///
public IReadOnlyList ShotTypes => _shotTypes;
#endregion
#region Implicit IUxrPrecacheable
///
public IEnumerable PrecachedInstances
{
get
{
foreach (UxrShotDescriptor shotType in _shotTypes)
{
if (shotType.PrefabInstantiateOnImpact)
{
yield return shotType.PrefabInstantiateOnImpact;
}
if (shotType.PrefabInstantiateOnTipWhenShot)
{
yield return shotType.PrefabInstantiateOnTipWhenShot;
}
if (shotType.PrefabScenarioImpactDecal && shotType.PrefabScenarioImpactDecal.gameObject)
{
yield return shotType.PrefabScenarioImpactDecal.gameObject;
}
if (shotType.ProjectilePrefab)
{
yield return shotType.ProjectilePrefab;
}
}
}
}
#endregion
#region Public Methods
///
/// Tries to get the that holds the that has the
/// component.
///
/// Actor component or null if it wasn't found
public UxrActor TryGetWeaponOwner()
{
UxrWeapon weapon = GetComponentInParent();
if (weapon)
{
return weapon.Owner;
}
return GetComponentInParent();
}
///
/// Shoots a round.
///
/// Index in , telling which shot type to fire
public void Shoot(int shotTypeIndex)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
Shoot(shotTypeIndex, _shotTypes[shotTypeIndex].ShotSource.position, _shotTypes[shotTypeIndex].ShotSource.rotation);
}
}
///
/// Shoots a round, overriding the source position and orientation.
///
/// Index in , telling which shot type to fire
/// Source shot position
/// Shot source orientation. The shot will be fired in the z (forward) direction
public void Shoot(int shotTypeIndex, Vector3 projectileSource, Quaternion projectileOrientation)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipWhenShot)
{
GameObject newInstance = Instantiate(_shotTypes[shotTypeIndex].PrefabInstantiateOnTipWhenShot, _shotTypes[shotTypeIndex].Tip.position, _shotTypes[shotTypeIndex].Tip.rotation);
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipParent)
{
newInstance.transform.parent = transform;
}
else
{
newInstance.transform.parent = null;
}
if (_shotTypes[shotTypeIndex].PrefabInstantiateOnTipLife >= 0.0f)
{
Destroy(newInstance, _shotTypes[shotTypeIndex].PrefabInstantiateOnTipLife);
}
}
UxrWeaponManager.Instance.RegisterNewProjectileShot(this, _shotTypes[shotTypeIndex], projectileSource, projectileOrientation);
if (_weaponAnimator != null && string.IsNullOrEmpty(_shotTypes[shotTypeIndex].ShotAnimationVarName) == false)
{
_weaponAnimator.SetTrigger(_shotTypes[shotTypeIndex].ShotAnimationVarName);
}
}
}
///
/// Shoots a round pointing to the given target.
///
/// Index in , telling which shot type to fire
/// Position where the shot will be going towards
public void ShootTo(int shotTypeIndex, Vector3 target)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
Vector3 direction = (target - _shotTypes[shotTypeIndex].ShotSource.position).normalized;
Shoot(shotTypeIndex, _shotTypes[shotTypeIndex].ShotSource.position, Quaternion.LookRotation(direction));
}
}
///
/// Gets the distance where a shot using the current position and orientation will impact.
///
/// Index in , telling which shot type to use
/// Shot distance or a negative value telling the current target is out of range
public float ShotRaycastDistance(int shotTypeIndex)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
if (Physics.Raycast(_shotTypes[shotTypeIndex].ShotSource.position,
_shotTypes[shotTypeIndex].ShotSource.forward,
out RaycastHit raycastHit,
_shotTypes[shotTypeIndex].ProjectileMaxDistance,
_shotTypes[shotTypeIndex].CollisionLayerMask,
QueryTriggerInteraction.Ignore))
{
return raycastHit.distance;
}
}
return -1.0f;
}
///
/// Gets the current world-space origin of projectiles fired using the given shot type.
///
/// Index in , telling which shot type to use
/// Projectile world-space source
public Vector3 GetShotOrigin(int shotTypeIndex)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
return _shotTypes[shotTypeIndex].ShotSource.position;
}
return Vector3.zero;
}
///
/// Gets the current world-space direction of projectiles fired using the given shot type.
///
/// Index in , telling which shot type to use
/// Projectile world-space direction
public Vector3 GetShotDirection(int shotTypeIndex)
{
if (shotTypeIndex >= 0 && shotTypeIndex < _shotTypes.Count)
{
return _shotTypes[shotTypeIndex].ShotSource.forward;
}
return Vector3.zero;
}
#endregion
}
}