fix(combat): 统一受击无敌闸门并修复 DoT 失效

将无敌判定收口到 PlayerController.TakeDamage(DamageInfo) 单一闸门并认 DamageFlags.IgnoreIFrame,PlayerStats 拆出原始扣血 ApplyDamage;新增 DamageFlags.IsDoT,持续伤害只扣血不打断硬直、不授予无敌。修复 DoT 绕过 HurtBox 时被 flag-blind 的整数 TakeDamage 静默吞没、且 FinalDamage 未结算(恒为0)的双重失效;敌人侧 DoT 标记 IsDoT 避免每 Tick 重置硬直。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:30:56 +08:00
parent 523f7c842a
commit 0491e3f919
8 changed files with 54 additions and 15 deletions

View File

@@ -94,23 +94,30 @@ namespace BaseGames.Player.States
public void TakeDamage(DamageInfo info)
{
if (_stats == null) return;
_stats.TakeDamage(info.FinalDamage);
if (_stats == null || !_stats.IsAlive) return;
// 当前状态标记为无敌(如旧版冲刺状态),或 Stats 层无敌窗口仍激活
// (冲刺无敌帧 DashInvincibilityDuration 内:跳过受击硬直;窗口过期后可被打断)
if (_currentState?.IsInvincible == true || (_stats != null && _stats.IsInvincible)) return;
// ── 唯一无敌闸门(认 flag──────────────────────────────────────
// 状态级无敌(冲刺)或 Stats 级无敌窗口激活时,普通伤害被挡;
// 携带 IgnoreIFrame 的伤害DoT / 陷阱 / 环境)穿透无敌。
bool invincible = (_currentState?.IsInvincible == true) || _stats.IsInvincible;
if (invincible && !info.Flags.HasFlag(DamageFlags.IgnoreIFrame)) return;
if (_stats.IsAlive)
{
GetState<HurtState>()?.Initialize(info);
TransitionTo(GetState<HurtState>());
}
else
// 原始扣血(无敌判定已在上面完成,避免下游再做 flag-blind 拦截)
_stats.ApplyDamage(info.FinalDamage);
if (!_stats.IsAlive)
{
TransitionTo(GetState<DeadState>());
_onPlayerDied?.Raise();
return;
}
// ── 持续伤害DoT只扣血不打断硬直、不授予无敌帧 ──────────
if (info.Flags.HasFlag(DamageFlags.IsDoT)) return;
// 一次性打击进入受击硬直HurtState.OnStateEnter 内授予慈悲无敌帧)
GetState<HurtState>()?.Initialize(info);
TransitionTo(GetState<HurtState>());
}
// ── IPoiseSource 实现(架构 06_CombatModule §13─────────────────────