Files
beyond/Assets/ThirdParty/Invector-3rdPersonController/Basic Locomotion/Scripts/Generic/Health/vHealthController.cs

242 lines
9.3 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events; // Nadal potrzebne dla UnityEvent ogólnego użytku
namespace Invector
{
// Prawdopodobnie te typy są zdefiniowane gdzieś globalnie w namespace Invector
// lub w plikach interfejsów. Jeśli nie, trzeba by je tu zdefiniować,
// ale zakładam, że kompilator je znajdzie, skoro vDamageReceiver ich używa.
// [System.Serializable] public class OnReceiveDamage : UnityEvent<vDamage> { } // Jeśli potrzebna definicja
// [System.Serializable] public class OnDead : UnityEvent<GameObject> { } // Jeśli potrzebna definicja
[vClassHeader("HealthController", iconName = "HealthControllerIcon")]
public class vHealthController : vMonoBehaviour, vIHealthController // Upewnij się, że to jest poprawny interfejs
{
#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;
if (onChangeHealth != null) onChangeHealth.Invoke(_currentHealth);
}
if (!_isDead && _currentHealth <= 0) { isDead = true; }
else if (isDead && _currentHealth > 0) { isDead = false; }
}
}
public virtual bool isDead
{
get
{
if (!_isDead && _currentHealth <= 0)
{
_isDead = true;
if (_onDead != null) _onDead.Invoke(gameObject);
}
return _isDead;
}
set
{
if (_isDead != value)
{
_isDead = value;
if (_isDead)
{
if (_onDead != null) _onDead.Invoke(gameObject);
}
}
}
}
public float healthRecovery = 0f;
public float healthRecoveryDelay = 0f;
[HideInInspector]
public float currentHealthRecoveryDelay;
[vEditorToolbar("Events", order = 100)]
public List<CheckHealthEvent> checkHealthEvents = new List<CheckHealthEvent>();
// Używamy typów zdarzeń zdefiniowanych przez Invector (OnReceiveDamage, OnDead)
[SerializeField] protected OnReceiveDamage _onStartReceiveDamage = new OnReceiveDamage();
[SerializeField] protected OnReceiveDamage _onReceiveDamage = new OnReceiveDamage();
[SerializeField] protected OnDead _onDead = new OnDead(); // Zakładając, że typ OnDead istnieje
[System.Serializable]
public class ValueChangedEvent : UnityEvent<float> { }
public ValueChangedEvent onChangeHealth = new ValueChangedEvent();
public UnityEvent onResetHealth = new UnityEvent(); // Standardowy UnityEvent
internal bool inHealthRecovery;
// Właściwości implementujące interfejs, używając typów Invectora
public OnReceiveDamage onStartReceiveDamage { get { return _onStartReceiveDamage; } } // Usunięto 'protected set' aby pasowało do get-only interfejsu
public OnReceiveDamage onReceiveDamage { get { return _onReceiveDamage; } } // Usunięto 'protected set'
public OnDead onDead { get { return _onDead; } } // Usunięto 'protected set'
#endregion
protected virtual void Start()
{
if (fillHealthOnStart) currentHealth = maxHealth;
currentHealthRecoveryDelay = healthRecoveryDelay;
}
protected virtual bool canRecoverHealth
{
get { return (_currentHealth >= 0 && healthRecovery > 0 && _currentHealth < maxHealth && !_isDead); }
}
protected virtual IEnumerator RecoverHealth()
{
inHealthRecovery = true;
while (currentHealthRecoveryDelay > 0 && !_isDead)
{
currentHealthRecoveryDelay -= Time.deltaTime;
yield return null;
}
while (canRecoverHealth)
{
HealthRecovery();
yield return null;
}
inHealthRecovery = false;
}
protected virtual void HealthRecovery()
{
if (!canRecoverHealth) return;
if (_currentHealth < maxHealth)
{
_currentHealth += healthRecovery * Time.deltaTime;
_currentHealth = Mathf.Min(_currentHealth, maxHealth);
if (onChangeHealth != null) onChangeHealth.Invoke(_currentHealth);
}
}
public virtual void AddHealth(int value) { currentHealth += value; }
public virtual void ChangeHealth(int value) { currentHealth = value; }
public virtual void ResetHealth(float health)
{
currentHealth = Mathf.Clamp(health, 0, maxHealth);
if (onResetHealth != null) onResetHealth.Invoke();
if (_isDead && _currentHealth > 0) isDead = false;
}
public virtual void ResetHealth()
{
currentHealth = maxHealth;
if (onResetHealth != null) onResetHealth.Invoke();
if (_isDead) isDead = false;
}
public virtual void ChangeMaxHealth(int value)
{
maxHealth += value;
if (maxHealth < 0) maxHealth = 0;
if (_currentHealth > maxHealth) currentHealth = maxHealth;
}
public virtual void SetHealthRecovery(float value)
{
healthRecovery = value;
if (!inHealthRecovery && canRecoverHealth && gameObject.activeInHierarchy)
{
StartCoroutine(RecoverHealth());
}
}
public virtual void TakeDamage(vDamage damage)
{
if (damage != null && !_isDead)
{
if (inHealthRecovery)
{
StopCoroutine(RecoverHealth());
inHealthRecovery = false;
}
currentHealthRecoveryDelay = healthRecoveryDelay;
// Użyj flagi ignoreAllHitEffects zdefiniowanej w Twoim zmodyfikowanym vDamage.cs
if (_onStartReceiveDamage != null && !damage.ignoreAllHitEffects)
{
_onStartReceiveDamage.Invoke(damage);
}
if (_currentHealth > 0 && !isImmortal)
{
_currentHealth -= damage.damageValue;
if (onChangeHealth != null) onChangeHealth.Invoke(_currentHealth);
}
// Użyj flagi ignoreAllHitEffects zdefiniowanej w Twoim zmodyfikowanym vDamage.cs
if (damage.damageValue > 0 && _onReceiveDamage != null && !damage.ignoreAllHitEffects)
{
_onReceiveDamage.Invoke(damage);
}
if (_currentHealth <= 0 && !_isDead)
{
isDead = true;
}
HandleCheckHealthEvents();
if (!_isDead && healthRecovery > 0 && !inHealthRecovery && gameObject.activeInHierarchy)
{
StartCoroutine(RecoverHealth());
}
}
}
protected virtual void HandleCheckHealthEvents()
{
if (checkHealthEvents == null) return;
for (int i = 0; i < checkHealthEvents.Count; i++)
{
var e = checkHealthEvents[i];
if (e == null || e.OnCheckHealth == null) continue;
bool conditionMet = false;
switch (e.healthCompare)
{
case CheckHealthEvent.HealthCompare.Equals:
conditionMet = Mathf.Approximately(_currentHealth, e.healthToCheck);
break;
case CheckHealthEvent.HealthCompare.HigherThan:
conditionMet = _currentHealth > e.healthToCheck;
break;
case CheckHealthEvent.HealthCompare.LessThan:
conditionMet = _currentHealth < e.healthToCheck;
break;
}
if (conditionMet)
{
e.OnCheckHealth.Invoke();
}
}
}
[System.Serializable]
public class CheckHealthEvent
{
public float healthToCheck;
public bool disableEventOnCheck;
public enum HealthCompare { Equals, HigherThan, LessThan }
public HealthCompare healthCompare = HealthCompare.Equals;
public UnityEvent OnCheckHealth = new UnityEvent();
}
}
}