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 { } // Jeśli potrzebna definicja // [System.Serializable] public class OnDead : UnityEvent { } // 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 checkHealthEvents = new List(); // 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 { } 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(); } } }