引入属性、伤害管线

This commit is contained in:
2026-09-18 21:43:22 +08:00
parent 92e1ea0d55
commit ba25ec1926
108 changed files with 2920 additions and 720 deletions
@@ -13,23 +13,17 @@ namespace SepCore.Components
private StatComponent _statComponent;
private StatProperty _maxHealthStat;
private Action<modifier, bool> _maxHealthStatCallback;
public StatProperty MaxHealthStat => _maxHealthStat;
private StatProperty _defenseStat;
private Action<modifier, bool> _defenseStatCallback;
public StatProperty DefenseStat => _defenseStat;
private StatProperty _dodgeStat;
private Action<modifier, bool> _dodgeStatCallback;
public StatProperty DodgeStat => _dodgeStat;
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 => Mathf.CeilToInt((_maxHealthBase + _maxHealthStat.Value) * _maxHealthStat.Percent);
public int MaxHealth => _statComponent != null
? Mathf.CeilToInt(_statComponent.GetStatValue(StatType.MaxHp))
: _maxHealthBase;
public int CurrentHealth
{
@@ -46,82 +40,115 @@ namespace SepCore.Components
_statComponent = statComponent;
if (_statComponent != null)
{
_maxHealthStat = _statComponent.GetStat(StatType.MaxHealth);
_maxHealthStatCallback = (modifier, isApply) =>
{
_statComponent.UpdateStat(_maxHealthStat, modifier, isApply);
GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(0, CurrentHealth, MaxHealth));
};
_statComponent.Subscribe(StatType.MaxHealth, _maxHealthStatCallback);
_defenseStat = _statComponent.GetStat(StatType.Defense);
_defenseStatCallback = (modifier, isApply) =>
_statComponent.UpdateStat(_defenseStat, modifier, isApply);
_statComponent.Subscribe(StatType.Defense, _defenseStatCallback);
_dodgeStat = _statComponent.GetStat(StatType.Dodge);
_dodgeStatCallback = (modifier, isApply) =>
_statComponent.UpdateStat(_dodgeStat, modifier, isApply);
_statComponent.Subscribe(StatType.Dodge, _dodgeStatCallback);
}
else
{
_maxHealthStat = new StatProperty();
_defenseStat = new StatProperty();
_dodgeStat = new StatProperty();
_statComponent.Pipeline.SetBaseValue(StatType.MaxHp, maxHealth);
_onStatChangedAction = OnStatChanged;
_statComponent.Pipeline.OnStatChanged += _onStatChangedAction;
}
}
public void OnReset()
{
if (_statComponent != null)
if (_statComponent != null && _onStatChangedAction != null)
{
_statComponent.Unsubscribe(StatType.MaxHealth, _maxHealthStatCallback);
_maxHealthStatCallback = null;
_statComponent.Unsubscribe(StatType.Defense, _defenseStatCallback);
_defenseStatCallback = null;
_statComponent.Unsubscribe(StatType.Dodge, _dodgeStatCallback);
_dodgeStatCallback = 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)
public void TakeDamage(int damage, bool isCrit = false, bool isDodge = false, Vector3? hitPosition = null)
{
if (_isDead) return;
float maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
if (UnityEngine.Random.value < Mathf.Clamp(_dodgeStat.Value, 0f, maxDodge))
if (isDodge)
{
damage = 0;
}
else
{
int defenseValue = (int)_defenseStat.Value;
float defensePercent = _defenseStat.Percent;
damage -= defenseValue;
damage = Mathf.CeilToInt(damage / defensePercent);
int minDamage = GameEntry.Luban.Global.MinDamage;
if (damage < minDamage) damage = minDamage;
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;
}
_currentHealth -= damage;
CauseDamage(damage);
if (_isPlayer)
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));
}
}
private void CauseDamage(int damage)
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.Global.DamageTextYOffset / 1000f;
GameEntry.DamageText.ShowDamage(transform.position + Vector3.up * yOffset, damage);
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);
}
}
}
}