using SepCore.Definition;
using UnityEngine;
namespace SepCore.Battle
{
///
/// 伤害结算管线(纯 C# 静态计算器)。
/// 实现武器类型加成、百分比放大、闪避判定、暴击判定、双曲护甲折减、保底伤害与生命偷取判定。
///
public static class DamagePipeline
{
public const float ArmorDivisor = 15f; // Brotato 双曲护甲基数
public const float DefaultCritMultiplier = 1.5f;
public const float DefaultBaseCrit = 0.05f;
///
/// 计算最终伤害结果。
///
public static DamageResult Calculate(in DamageContext context)
{
// 1. 闪避判定 (Defender Dodge)
float dodgeChance = context.DefenderPipeline != null
? context.DefenderPipeline.GetValue(StatType.Dodge)
: context.FallbackDefenderDodge;
float maxDodge = 0.8f;
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
StatConfig dodgeConfig = GameEntry.Luban.Get((int)StatType.Dodge);
if (dodgeConfig != null && dodgeConfig.HasMax)
{
maxDodge = dodgeConfig.MaxValue;
}
else
{
maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
}
}
dodgeChance = Mathf.Clamp(dodgeChance, 0f, maxDodge);
if (dodgeChance > 0f && Random.value < dodgeChance)
{
return new DamageResult
{
Damage = 0,
IsDodge = true,
IsCrit = false,
IsLifeSteal = false,
LifeStealAmount = 0
};
}
// 2. 武器类型伤害加成 (Melee / Ranged / Elemental)
float rawDamage = context.BaseDamage;
if (context.AttackerPipeline != null && context.DamageScalingStat != StatType.None)
{
float scalingBonus = context.AttackerPipeline.GetValue(context.DamageScalingStat);
float ratio = context.ScalingRatio > 0f ? context.ScalingRatio : 1f;
rawDamage += scalingBonus * ratio;
}
rawDamage = Mathf.Max(rawDamage, 1f);
// 3. 全局百分比增伤放大 (DamagePercent)
float damagePercent = context.AttackerPipeline != null
? context.AttackerPipeline.GetValue(StatType.DamagePercent)
: 1f;
// 允许百分比降至 0,防止负向修饰(如 -100% 增伤/重度虚弱)被错误重置为 1 倍全额伤害
damagePercent = Mathf.Max(0f, damagePercent);
float amplifiedDamage = rawDamage * damagePercent;
amplifiedDamage = Mathf.Max(amplifiedDamage, 0f);
// 4. 暴击判定 (CritChance & CritMultiplier)
float attackerCrit = context.AttackerPipeline != null
? context.AttackerPipeline.GetValue(StatType.CritChance)
: 0f;
float baseCrit = context.BaseCritChance;
float maxCrit = 0.9f;
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
StatConfig critConfig = GameEntry.Luban.Get((int)StatType.CritChance);
if (critConfig != null && critConfig.HasMax)
{
maxCrit = critConfig.MaxValue;
}
}
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, maxCrit);
bool isCrit = totalCritChance > 0f && Random.value < totalCritChance;
float critMultiplier = context.CritMultiplier;
if (critMultiplier <= 0f)
{
critMultiplier = GameEntry.Luban != null && GameEntry.Luban.IsReady
? GameEntry.Luban.Global.CritMultiplier
: DefaultCritMultiplier;
}
float damageAfterCrit = isCrit ? (amplifiedDamage * critMultiplier) : amplifiedDamage;
// 5. Brotato 标准双曲护甲折减 (Armor Mitigation)
float armor = context.DefenderPipeline != null
? context.DefenderPipeline.GetValue(StatType.Armor)
: context.FallbackDefenderArmor;
float mitigation;
if (armor >= 0f)
{
mitigation = 1f / (1f + armor / ArmorDivisor);
}
else
{
// 负护甲易伤放大: 2 - 1/(1 - armor/15)
mitigation = 2f - (1f / (1f - armor / ArmorDivisor));
}
float finalDamageFloat = damageAfterCrit * mitigation;
int minDamage = 1;
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
minDamage = GameEntry.Luban.Global.MinDamage;
}
int finalDamage = Mathf.Max(Mathf.CeilToInt(finalDamageFloat), minDamage);
// 6. 生命偷取判定 (LifeSteal)
bool isLifeSteal = false;
int lifeStealAmount = 0;
if (finalDamage > 0 && context.AttackerPipeline != null)
{
float lifeStealRate = context.AttackerPipeline.GetValue(StatType.LifeSteal);
if (lifeStealRate > 0f && Random.value < lifeStealRate)
{
isLifeSteal = true;
lifeStealAmount = 1;
}
}
return new DamageResult
{
Damage = finalDamage,
IsDodge = false,
IsCrit = isCrit,
IsLifeSteal = isLifeSteal,
LifeStealAmount = lifeStealAmount
};
}
}
}