Files
zeling_v2/Assets/_Game/Scripts/Combat/CombatInterfaces.cs
T
joywayerandClaude Opus 5 bf7899f91a feat(combat): 接通弹反反制——弹反成功让发起攻击的敌人硬直
EnemyBase.ReceiveParry 早就存在且形态正确(强制 Stagger + 打断全部能力 +
定时恢复,小怪 Boss 通用),但一直零调用者:弹反判定点拿不到攻击者引用。
硬直时长的权威也早就存在——ParryConfigSO.StaggerDuration,注释写着
「被弹反敌人的受击硬直时长」,同样零消费者。本次只是把这两端接上,没有新造语义。

接缝选在 DamageInfo 而非"弹反成功时回调 HitBox"(spec §6.2 留的两个选项):
攻击者引用与既有的 SourceProjectile 完全同构——都是"攻击从哪来"的引用,
供弹反分支反向作用于来源。落到代码上就是弹反分支里并列的两行,不引入第二条回调链路。

- Combat 新增 IParryable(攻击被弹反时的反制承受方)。用接口是因为程序集方向:
  Combat 不能反向依赖 Enemies,而 HurtBox.ReceiveDamage 是唯一同时握有
  弹反结果与 DamageInfo 的地方。与 IDamageable 同款做法。
- DamageInfo 加 Attacker 字段([NonSerialized],Builder 与 From 工厂同步支持)。
- HitBox 在 Activate 时按宿主解析并缓存 IParryable——与 _ownerRigidbody 同源、
  与 _ownerProjectile 同手法。放 Activate 而非 Awake:attacker 可由调用方传入。
- ParrySystem 暴露 StaggerDuration,HurtBox 在弹反成功时读它,不另立常量。
- EnemyBase 声明实现 IParryable(方法签名本就匹配,无需改实现)。

顺带确认旧的全局误伤路径确实已随 Boss 单轨合并删净:全库不再有
HandleParrySuccess,ParryInfoEventChannelSO 只剩 ParrySystem 自己 Raise,
不存在"弹反小怪导致场上 Boss 一起硬直"的双重路径。

验证:编译 0 错;EditMode 276/276(273 + 新增 3)。
测试走完整 HurtBox 流水线:反射仅用于喂配置与跑真实 Awake,被测逻辑全程生产代码
(与 BossPhaseAbilityGateTests 同一手法)。硬直时长断言取 1.25f 这个非默认值,
才能证明读的是配置而非常量。用例把完美弹反阈值设为负数以避开会改 Time.timeScale
的子弹时间协程(编辑模式下它 yield 后不会恢复),TearDown 另有兜底还原。
变异验证——注释掉 HurtBox 里那一行后,恰好只有反制用例变红;还原后复验 276/276。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:36:52 +08:00

64 lines
2.2 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.
namespace BaseGames.Combat
{
/// <summary>
/// 可受击实体接口。PlayerController 和 EnemyBase 实现此接口。
/// HurtBox.Awake 通过 GetComponentInParent&lt;IDamageable&gt;() 注入。
/// </summary>
public interface IDamageable
{
bool IsAlive { get; }
bool IsInvincible { get; }
int Defense { get; }
void TakeDamage(DamageInfo info);
}
/// <summary>
/// 攻击被弹反时的反制承受方(攻击发起者实现,如 EnemyBase)。
///
/// 存在的理由是程序集方向:Combat 不能反向依赖 Enemies,而弹反成功的判定点
/// <see cref="HurtBox.ReceiveDamage"/>)是唯一同时握有弹反结果与
/// <see cref="DamageInfo"/> 的地方,所以攻击者只能以接口形态经 DamageInfo 传进来。
/// 与 <see cref="IDamageable"/> 同款做法。
/// </summary>
public interface IParryable
{
/// <summary>本方发起的攻击被弹反:进入硬直 <paramref name="staggerDuration"/> 秒。</summary>
void ReceiveParry(float staggerDuration);
}
/// <summary>
/// 可持有霸体的实体接口。HurtBox 在 ReceiveDamage 中做等级比较。
/// </summary>
public interface IPoiseSource
{
PoiseLevel GetCurrentPoiseLevel();
}
/// <summary>
/// 护盾接口(玩家专属)。由 PlayerController.Awake() 注入 HurtBox。
/// AbsorbDamage 返回穿透量(0 = 全部吸收,>0 = 穿透量继续走 TakeDamage 流程)。
/// </summary>
public interface IShieldable
{
bool HasShield { get; }
int AbsorbDamage(int amount);
}
/// <summary>
/// 可破坏机关/障碍物接口。HitBox 在命中非 HurtBox 对象时尝试调用。
/// </summary>
public interface IBreakable
{
void TryInteract(DamageInfo info);
}
/// <summary>
/// 可施加状态效果的实体接口(避免 Combat 直接引用 StatusEffects 程序集)。
/// StatusEffectManager 实现此接口;HurtBox.ReceiveDamage 步骤 8 通过此接口调用。
/// </summary>
public interface IStatusEffectable
{
void ApplyStatusEffect(DamageType type);
}
}