// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.Mechanics.Weapons { /// /// An actor in the Weapons module is an entity that can inflict and/or take damage. /// public class UxrActor : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private float _life; [SerializeField] private Animator _animator; [SerializeField] private string _takeDamageAnimationTriggerVarName; [SerializeField] private string _dieAnimationTriggerVarName; [SerializeField] private AudioClip _takeDamageAudioClip; [SerializeField] private AudioClip _dieAudioClip; [SerializeField] private float _destroyAfterDeadSeconds = -1.0f; [SerializeField] private bool _automaticDamageHandling = true; [SerializeField] private bool _automaticDeadHandling = true; #endregion #region Public Types & Data /// /// Event triggered right before the actor is about to receive damage. /// Setting will allow not to take the damage. /// public event EventHandler DamageReceiving; /// /// Event triggered right after the actor received damage. /// Setting is not supported, since the damage was already taken. /// public event EventHandler DamageReceived; /// /// Gets or sets whether damage should be handled automatically. Automatic damage handling will take care of computing /// the new life value when receiving damage. /// public bool AutomaticDamageHandling { get => _automaticDamageHandling; set => _automaticDamageHandling = value; } /// /// Gets or sets whether to handle death automatically when the actor's life reaches zero. /// public bool AutomaticDeadHandling { get => _automaticDeadHandling; set => _automaticDeadHandling = value; } /// /// Gets or sets the actor's life value. /// public float Life { get => _life; set => _life = value; } /// /// Gets whether the actor is dead. /// public bool IsDead { get; private set; } #endregion #region Public Methods /// /// Makes the actor receive a damaging projectile impact. /// /// Actor source of the projectile /// Raycast that hit the actor /// Damage to be taken public void ReceiveImpact(UxrActor actorSource, RaycastHit raycastHit, float damage) { OnReceiveDamage(new UxrDamageEventArgs(actorSource, this, raycastHit, damage, damage >= Life)); } /// /// Makes the actor receive explosive damage. /// /// Actor source of the projectile /// Explosion source /// Damage to be taken public void ReceiveExplosion(UxrActor actorSource, Vector3 position, float damage) { OnReceiveDamage(new UxrDamageEventArgs(actorSource, this, position, damage, damage >= Life)); } /// /// Makes the actor receive generic damage. /// /// Damage to be taken public void ReceiveDamage(float damage) { OnReceiveDamage(new UxrDamageEventArgs(damage, damage >= Life)); } /// /// Forces the actor to die after a certain amount of seconds. /// /// Seconds to wait for the actor to die public void Die(float delaySeconds) { Invoke(nameof(DieInternal), delaySeconds); } #endregion #region Unity /// /// Makes sure the singleton instance is available so that actors are registered."/> /// protected override void Awake() { base.Awake(); UxrWeaponManager.Instance.Poke(); } #endregion #region Event Trigger Methods /// /// Handles receiving damage and calls the appropriate events. /// /// Damage event parameters private void OnReceiveDamage(UxrDamageEventArgs e) { if (IsDead) { return; } DamageReceiving?.Invoke(this, e); if (!e.IsCanceled) { bool destroy = false; if (_automaticDamageHandling) { _life -= e.Damage; } if (_life <= 0.0f) { // Deadly damage if (_automaticDeadHandling) { destroy = true; } else { IsDead = true; } } else { // Non-deadly damage if (_animator != null && string.IsNullOrEmpty(_takeDamageAnimationTriggerVarName) == false) { _animator.SetTrigger(_takeDamageAnimationTriggerVarName); } if (_takeDamageAudioClip) { AudioSource.PlayClipAtPoint(_takeDamageAudioClip, transform.position); } } DamageReceived?.Invoke(this, e); if (destroy) { DieInternal(); } } } #endregion #region Private Methods /// /// Makes the actor die. /// private void DieInternal() { Life = 0.0f; IsDead = true; if (_animator != null && string.IsNullOrEmpty(_dieAnimationTriggerVarName) == false) { _animator.SetTrigger(_dieAnimationTriggerVarName); } if (_dieAudioClip) { AudioSource.PlayClipAtPoint(_dieAudioClip, transform.position); } Destroy(gameObject, _destroyAfterDeadSeconds > 0.0f ? _destroyAfterDeadSeconds : 0.0f); } #endregion } }