using System; using SepCore.Event; using SepCore.Definition; using UnityEngine; namespace SepCore.Components { public class HealthComponent : MonoBehaviour { private int _maxHealthBase; private int _currentHealth; private bool _isDead => _currentHealth <= 0; private StatComponent _statComponent; private Action _onStatChangedAction; private float _lastLifeStealTime = -1f; public const float LifeStealIcd = 0.1f; private bool _isPlayer => _statComponent != null; public float HealthRatio => MaxHealth > 0 ? (float)CurrentHealth / MaxHealth : 0f; public int MaxHealth => _statComponent != null ? Mathf.CeilToInt(_statComponent.GetStatValue(StatType.MaxHp)) : _maxHealthBase; public int CurrentHealth { get => _currentHealth; set => _currentHealth = value; } #region FSM public void OnInit(int maxHealth, StatComponent statComponent = null) { _maxHealthBase = maxHealth; _currentHealth = maxHealth; _statComponent = statComponent; if (_statComponent != null) { _statComponent.Pipeline.SetBaseValue(StatType.MaxHp, maxHealth); _onStatChangedAction = OnStatChanged; _statComponent.Pipeline.OnStatChanged += _onStatChangedAction; } } public void OnReset() { if (_statComponent != null && _onStatChangedAction != null) { _statComponent.Pipeline.OnStatChanged -= _onStatChangedAction; _onStatChangedAction = null; } _statComponent = null; _lastLifeStealTime = -1f; } private void OnStatChanged(StatType statType, float newValue) { if (statType == StatType.MaxHp) { if (_currentHealth > MaxHealth) { _currentHealth = MaxHealth; } if (GameEntry.Event != null) { GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(0, CurrentHealth, MaxHealth)); } } } #endregion public void TakeDamage(int damage, bool isCrit = false, bool isDodge = false, Vector3? hitPosition = null) { if (_isDead) return; if (isDodge) { float yOffset = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.DamageTextYOffset / 1000f : 1f; Vector3 pos = hitPosition ?? (transform.position + Vector3.up * yOffset); if (GameEntry.DamageText != null) { GameEntry.DamageText.ShowDamage(pos, 0, false, true); } return; } if (damage <= 0) return; _currentHealth = Mathf.Max(0, _currentHealth - damage); CauseDamage(damage, isCrit, hitPosition); if (_isPlayer && GameEntry.Event != null) { GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(damage, CurrentHealth, MaxHealth)); } } public void TakeDamage(int damage) { TakeDamage(damage, false, false, null); } public void Heal(int healAmount) { if (_isDead || healAmount <= 0) return; int oldHealth = _currentHealth; _currentHealth = Mathf.Min(_currentHealth + healAmount, MaxHealth); int healed = _currentHealth - oldHealth; if (healed > 0 && _isPlayer && GameEntry.Event != null) { GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(-healed, CurrentHealth, MaxHealth)); } } public bool TryTriggerLifeSteal(int healAmount, float? customTime = null) { if (_isDead || healAmount <= 0) return false; float currentTime = customTime ?? Time.time; if (_lastLifeStealTime >= 0f && currentTime - _lastLifeStealTime < LifeStealIcd) { return false; } _lastLifeStealTime = currentTime; Heal(healAmount); return true; } private void CauseDamage(int damage, bool isCrit = false, Vector3? hitPosition = null) { if (damage <= 0) return; float yOffset = GameEntry.Luban != null && GameEntry.Luban.IsReady ? GameEntry.Luban.Global.DamageTextYOffset / 1000f : 1f; Vector3 pos = hitPosition ?? (transform.position + Vector3.up * yOffset); if (GameEntry.DamageText != null) { GameEntry.DamageText.ShowDamage(pos, damage, isCrit, false); } } } }