using UnityEngine; using BaseGames.Core.Events; namespace BaseGames.Combat { /// /// 单次伤害信息。流水线:RawDamage → Amount(护盾修改)→ FinalDamage(防御减免后)。 /// ⚠️ 保留为可变 struct:HurtBox 流水线需要在方法内修改本地副本的 Amount / FinalDamage。 /// Builder 通过独立字段构造,不直接修改 DamageInfo 实例。 /// [System.Serializable] public struct DamageInfo { public int RawDamage; // HitBox 设定的原始值(工厂/Builder 写入一次) public int Amount; // 流水线中被护盾/防御修改 public int FinalDamage; // HurtBox 写入,最终 HP 扣除量 public Vector2 KnockbackDirection; public float KnockbackForce; public float HitStunDuration; public DamageType Type; public DamageCategory Category; public DamageFlags Flags; public DamageTags Tags; public Vector2 SourcePosition; public int SourceLayer; public HitFxType FxType; public BreakLevel Break; public string SourceId; public string SkillId; /// /// HitBox 激活实例 ID(由 HitBox.Activate() 自动生成并写入,0 = 无追踪路径)。 /// HurtBoxOwnerGuard 用此字段做同激活去重,防止多 HurtBox 节点被同一次攻击重复扣血。 /// [NonSerialized]:每次激活动态生成,不需要序列化。 /// [System.NonSerialized] public uint HitActivationId; /// /// 攻击来源投射物(仅当攻击方是 Projectile 时非 null)。 /// 用于弹反成功时调用 ReflectAsPlayerProjectile() 翻转阵营。 /// [NonSerialized]:MonoBehaviour 引用不参与 Unity 资产序列化。 /// [System.NonSerialized] public Projectile SourceProjectile; // ── Builder ────────────────────────────────────────────────────────── /// /// 通过独立字段构造 DamageInfo,避免直接持有可变 DamageInfo 实例。 /// public class Builder { private int _raw; private DamageType _type; private DamageCategory _category; private DamageFlags _flags; private DamageTags _tags; private string _skillId; private string _sourceId; private Vector2 _knockbackDirection; private float _knockbackForce; private float _hitStunDuration; private HitFxType _fxType; private BreakLevel _break; private Vector2 _sourcePosition; private int _sourceLayer; private Projectile _sourceProjectile; public Builder() { } // SetRaw 同步初始化 Amount(Amount 始终以 RawDamage 为起点) public Builder SetRaw(int v) { _raw = v; return this; } public Builder SetType(DamageType v) { _type = v; return this; } public Builder SetCategory(DamageCategory v) { _category = v; return this; } public Builder SetFlags(DamageFlags v) { _flags = v; return this; } public Builder SetTags(DamageTags v) { _tags = v; return this; } public Builder SetSkillId(string v) { _skillId = v; return this; } public Builder SetSourceId(string v) { _sourceId = v; return this; } public Builder SetKnockback(Vector2 dir, float force) { _knockbackDirection = dir; _knockbackForce = force; return this; } public Builder SetStun(float dur) { _hitStunDuration = dur; return this; } public Builder SetFx(HitFxType v) { _fxType = v; return this; } public Builder SetBreak(BreakLevel v) { _break = v; return this; } public Builder SetSourcePos(Vector2 v) { _sourcePosition = v; return this; } public Builder SetLayer(int v) { _sourceLayer = v; return this; } public Builder SetProjectile(Projectile v) { _sourceProjectile = v; return this; } public DamageInfo Build() => new DamageInfo { RawDamage = _raw, Amount = _raw, Type = _type, Category = _category, Flags = _flags, Tags = _tags, SkillId = _skillId, SourceId = _sourceId, KnockbackDirection = _knockbackDirection, KnockbackForce = _knockbackForce, HitStunDuration = _hitStunDuration, FxType = _fxType, Break = _break, SourcePosition = _sourcePosition, SourceLayer = _sourceLayer, SourceProjectile = _sourceProjectile, }; } /// /// ⚡ 零堆分配工厂(热路径首选)。从 DamageSourceSO 填入所有静态字段; /// 可选传入运行时字段(knockbackDir、sourcePos、sourceLayer), /// 无需调用方事后就地赋值。 /// public static DamageInfo From( DamageSourceSO so, Vector2 knockbackDir = default, Vector2 sourcePos = default, int sourceLayer = 0, Projectile sourceProjectile = null) { int baseAmt = Mathf.RoundToInt(so.BaseDamage * so.DamageMultiplier); return new DamageInfo { RawDamage = baseAmt, Amount = baseAmt, Type = so.Type, Category = so.Category, Flags = so.Flags, Tags = so.Tags, HitStunDuration = so.HitStunDuration, FxType = so.FxType, Break = so.BreakLevel, SourceId = so.sourceId, SkillId = so.skillId, KnockbackDirection = knockbackDir, KnockbackForce = so.KnockbackForce, SourcePosition = sourcePos, SourceLayer = sourceLayer, SourceProjectile = sourceProjectile, }; } } /// 伤害事件频道(EVT_DamageDealt)。 [UnityEngine.CreateAssetMenu(menuName = "Events/DamageDealt")] public class DamageInfoEventChannelSO : BaseEventChannelSO { } }