Files
zeling_v2/Assets/Scripts/Combat/ShieldComponent.cs
2026-05-12 21:50:49 +08:00

140 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System;
using BaseGames.Core.Events;
namespace BaseGames.Combat
{
/// <summary>
/// 护盾组件。实现 IShieldable 接口供 HurtBox 注入。
/// 护盾参数通过 ShieldConfigSO 集中配置。
/// </summary>
public class ShieldComponent : MonoBehaviour, IShieldable
{
[Header("配置资产")]
[SerializeField] private ShieldConfigSO _config;
[Header("VFX 事件频道")]
[SerializeField] private VoidEventChannelSO _onShieldBrokenChannel; // 护盾破碎 → 播放破碎 VFX
[SerializeField] private VoidEventChannelSO _onShieldRestoredChannel; // 护盾恢复 → 播放恢复 VFX
// ── 运行时属性 ────────────────────────────────────────────────────────
private int _maxShieldHP;
public int MaxShieldHP => _maxShieldHP;
public int CurrentShieldHP { get; private set; }
/// <summary>当前是否能吸收伤害(护盾 HP > 0 且不在破碎惩罚期)。</summary>
public bool HasShield => CurrentShieldHP > 0 && _brokenPenaltyTimer <= 0f;
private float AbsorptionRatio => _config.DamageAbsorptionRatio;
private float RechargeDelay => _config.RechargeDelay;
private float RechargeRate => _config.RechargeRate;
private float BrokenPenaltyDur => _config.BrokenPenaltyDuration;
private float ParryRestoreRatio => _config.ParryRestoreRatio;
public event Action<int, int> OnShieldChanged; // (current, max)
public event Action OnShieldBroken;
private float _regenDelayTimer;
private float _brokenPenaltyTimer;
private void Awake()
{
Debug.Assert(_config != null, "[ShieldComponent] _config 未赋值,请在 Inspector 中指定 ShieldConfigSO。", this);
_maxShieldHP = _config != null ? _config.MaxShieldHP : 0;
CurrentShieldHP = MaxShieldHP;
}
private void Update()
{
int maxHP = MaxShieldHP;
if (maxHP <= 0) return;
// 破碎惩罚计时
if (_brokenPenaltyTimer > 0f)
{
_brokenPenaltyTimer -= Time.deltaTime;
// 破碎惩罚结束 → 广播护盾恢复事件VFX 钩子)
if (_brokenPenaltyTimer <= 0f)
_onShieldRestoredChannel?.Raise();
return;
}
if (CurrentShieldHP >= maxHP) return;
if (RechargeRate <= 0f) return;
if (_regenDelayTimer > 0f)
{
_regenDelayTimer -= Time.deltaTime;
return;
}
int prev = CurrentShieldHP;
CurrentShieldHP = Mathf.Min(CurrentShieldHP + Mathf.CeilToInt(RechargeRate * Time.deltaTime), maxHP);
if (CurrentShieldHP != prev)
OnShieldChanged?.Invoke(CurrentShieldHP, maxHP);
}
/// <summary>
/// 尝试以护盾吸收伤害。返回穿透量0=全部吸收,>0=穿透量继续走 TakeDamage 流程)。
/// </summary>
public int AbsorbDamage(int amount)
{
if (!HasShield) return amount;
_regenDelayTimer = RechargeDelay;
int maxHP = MaxShieldHP;
// 按吸收比例计算实际由护盾承担的伤害
int toAbsorb = Mathf.FloorToInt(amount * AbsorptionRatio);
toAbsorb = Mathf.Min(toAbsorb, CurrentShieldHP);
int passthrough = amount - toAbsorb;
CurrentShieldHP -= toAbsorb;
OnShieldChanged?.Invoke(CurrentShieldHP, maxHP);
if (CurrentShieldHP <= 0)
{
CurrentShieldHP = 0;
_brokenPenaltyTimer = BrokenPenaltyDur;
OnShieldBroken?.Invoke();
_onShieldBrokenChannel?.Raise(); // VFX 钩子:播放护盾破碎特效
}
return passthrough;
}
/// <summary>存档点 / 复活时调用:完全恢复护盾并清除惩罚状态。</summary>
public void FullRecharge()
{
bool wasBroken = _brokenPenaltyTimer > 0f || CurrentShieldHP <= 0;
_brokenPenaltyTimer = 0f;
_regenDelayTimer = 0f;
CurrentShieldHP = MaxShieldHP;
OnShieldChanged?.Invoke(CurrentShieldHP, MaxShieldHP);
if (wasBroken)
_onShieldRestoredChannel?.Raise(); // VFX 钩子:护盾已满血恢复
}
/// <summary>弹反成功时调用:按 ParryRestoreRatio 恢复护盾(会清除惩罚状态)。</summary>
public void OnParrySuccess()
{
int maxHP = MaxShieldHP;
if (maxHP <= 0) return;
_brokenPenaltyTimer = 0f;
_regenDelayTimer = 0f;
int restore = Mathf.CeilToInt(maxHP * ParryRestoreRatio);
CurrentShieldHP = Mathf.Min(CurrentShieldHP + restore, maxHP);
OnShieldChanged?.Invoke(CurrentShieldHP, maxHP);
}
/// <summary>Inspector / 道具系统调用:设置最大护盾并重置当前值。</summary>
public void SetMaxShieldHP(int max)
{
_maxShieldHP = Mathf.Max(0, max);
_brokenPenaltyTimer = 0f;
CurrentShieldHP = MaxShieldHP;
OnShieldChanged?.Invoke(CurrentShieldHP, MaxShieldHP);
}
}
}