Files
beyond/Assets/ThirdParty/Invector-3rdPersonController/Basic Locomotion/Scripts/Generic/Health/vHealthController.cs
2025-02-03 14:22:39 +01:00

268 lines
8.6 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using UnityEngine;
using UnityEngine.Events;
namespace Invector
{
[vClassHeader("HealthController", iconName = "HealthControllerIcon")]
public class vHealthController : vMonoBehaviour, vIHealthController
{
#region Variables
[vEditorToolbar("Health", order = 0)]
[SerializeField] [vReadOnly] protected bool _isDead;
[vBarDisplay("maxHealth")] [SerializeField] protected float _currentHealth;
public bool isImmortal = false;
[vHelpBox("If you want to start with different value, uncheck this and make sure that the current health has a value greater zero")]
public bool fillHealthOnStart = true;
public int maxHealth = 100;
public int MaxHealth
{
get
{
return maxHealth;
}
protected set
{
maxHealth = value;
}
}
public float currentHealth
{
get
{
return _currentHealth;
}
protected set
{
if (_currentHealth != value)
{
_currentHealth = value;
onChangeHealth.Invoke(_currentHealth);
}
if (!_isDead && _currentHealth <= 0)
{
//_isDead = true;
isDead = true;
onDead.Invoke(gameObject);
}
else if (isDead && _currentHealth > 0)
{
isDead = false;
}
}
}
public virtual bool isDead
{
get
{
if (!_isDead && currentHealth <= 0)
{
_isDead = true;
onDead.Invoke(gameObject);
}
return _isDead;
}
set
{
_isDead = value;
}
}
public float healthRecovery = 0f;
public float healthRecoveryDelay = 0f;
[HideInInspector]
public float currentHealthRecoveryDelay;
[vEditorToolbar("Events", order = 100)]
public List<CheckHealthEvent> checkHealthEvents = new List<CheckHealthEvent>();
[SerializeField] protected OnReceiveDamage _onStartReceiveDamage = new OnReceiveDamage();
[SerializeField] protected OnReceiveDamage _onReceiveDamage = new OnReceiveDamage();
[SerializeField] protected OnDead _onDead = new OnDead();
public ValueChangedEvent onChangeHealth;
public OnReceiveDamage onStartReceiveDamage { get { return _onStartReceiveDamage; } protected set { _onStartReceiveDamage = value; } }
public OnReceiveDamage onReceiveDamage { get { return _onReceiveDamage; } protected set { _onReceiveDamage = value; } }
public OnDead onDead { get { return _onDead; } protected set { _onDead = value; } }
public UnityEvent onResetHealth;
internal bool inHealthRecovery;
#endregion
protected virtual void Start()
{
if (fillHealthOnStart)
currentHealth = maxHealth;
currentHealthRecoveryDelay = healthRecoveryDelay;
}
protected virtual bool canRecoverHealth
{
get
{
return (currentHealth >= 0 && healthRecovery > 0 && currentHealth < maxHealth);
}
}
protected virtual IEnumerator RecoverHealth()
{
inHealthRecovery = true;
while (canRecoverHealth && !isDead)
{
HealthRecovery();
yield return null;
}
inHealthRecovery = false;
}
protected virtual void HealthRecovery()
{
if (!canRecoverHealth||isDead) return;
if (currentHealthRecoveryDelay > 0)
currentHealthRecoveryDelay -= Time.deltaTime;
else
{
if (currentHealth > maxHealth)
currentHealth = maxHealth;
if (currentHealth < maxHealth)
currentHealth += healthRecovery * Time.deltaTime;
}
}
/// <summary>
/// Increase or decrease currentHealth (Positive or Negative Values)
/// </summary>
/// <param name="value">Value to change</param>
public virtual void AddHealth(int value)
{
currentHealth += value;
currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
if (!isDead && currentHealth <= 0)
{
isDead = true;
onDead.Invoke(gameObject);
}
HandleCheckHealthEvents();
}
/// <summary>
/// Change the currentHealth of Character
/// </summary>
/// <param name="value"></param>
public virtual void ChangeHealth(int value)
{
currentHealth = value;
currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
if (!isDead && currentHealth <= 0)
{
isDead = true;
onDead.Invoke(gameObject);
}
HandleCheckHealthEvents();
}
/// <summary>
/// Reset's current health to specific health value
/// </summary>
/// <param name="health">target health</param>
public virtual void ResetHealth(float health)
{
currentHealth = health;
onResetHealth.Invoke();
if (isDead) isDead = false;
}
/// <summary>
/// Reset's current health to max health
/// </summary>
public virtual void ResetHealth()
{
currentHealth = maxHealth;
onResetHealth.Invoke();
if (isDead) isDead = false;
}
/// <summary>
/// Change the MaxHealth of Character
/// </summary>
/// <param name="value"></param>
public virtual void ChangeMaxHealth(int value)
{
maxHealth += value;
if (maxHealth < 0)
maxHealth = 0;
}
/// <summary>
/// Set a value to HealthRecovery to start recovering health
/// </summary>
/// <param name="value"></param>
public virtual void SetHealthRecovery(float value)
{
healthRecovery = value;
StartCoroutine(RecoverHealth());
}
/// <summary>
/// Apply Damage to Current Health
/// </summary>
/// <param name="damage">damage</param>
public virtual void TakeDamage(vDamage damage)
{
if (damage != null)
{
onStartReceiveDamage.Invoke(damage);
currentHealthRecoveryDelay = currentHealth <= 0 ? 0 : healthRecoveryDelay;
if (currentHealth > 0 && !isImmortal)
{
currentHealth -= damage.damageValue;
}
if (damage.damageValue > 0)
onReceiveDamage.Invoke(damage);
HandleCheckHealthEvents();
}
}
protected virtual void HandleCheckHealthEvents()
{
var events = checkHealthEvents.FindAll(e => (e.healthCompare == CheckHealthEvent.HealthCompare.Equals && currentHealth.Equals(e.healthToCheck)) ||
(e.healthCompare == CheckHealthEvent.HealthCompare.HigherThan && currentHealth > (e.healthToCheck)) ||
(e.healthCompare == CheckHealthEvent.HealthCompare.LessThan && currentHealth < (e.healthToCheck)));
for (int i = 0; i < events.Count; i++)
{
events[i].OnCheckHealth.Invoke();
}
if (currentHealth < maxHealth && this.gameObject.activeInHierarchy && !inHealthRecovery)
StartCoroutine(RecoverHealth());
}
[System.Serializable]
public class CheckHealthEvent
{
public int healthToCheck;
public bool disableEventOnCheck;
public enum HealthCompare
{
Equals,
HigherThan,
LessThan
}
public HealthCompare healthCompare = HealthCompare.Equals;
public UnityEngine.Events.UnityEvent OnCheckHealth;
}
[System.Serializable]
public class ValueChangedEvent : UnityEvent<float>
{
}
}
}