Files
vampire-like/Assets/GameMain/Scripts/Runtime/Components/HealthComponent.cs
T
2026-09-18 21:43:22 +08:00

156 lines
4.9 KiB
C#

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<StatType, float> _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);
}
}
}
}