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