242 lines
9.3 KiB
C#
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();
|
|
}
|
|
}
|
|
} |