// -------------------------------------------------------------------------------------------------------------------- // // 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 } }