135 lines
5.1 KiB
C#
135 lines
5.1 KiB
C#
using SepCore.Definition;
|
|
using UnityEngine;
|
|
|
|
namespace SepCore.Battle
|
|
{
|
|
/// <summary>
|
|
/// 伤害结算管线(纯 C# 静态计算器)。
|
|
/// 实现武器类型加成、百分比放大、闪避判定、暴击判定、双曲护甲折减、保底伤害与生命偷取判定。
|
|
/// </summary>
|
|
public static class DamagePipeline
|
|
{
|
|
public const float ArmorDivisor = 15f; // Brotato 双曲护甲基数
|
|
public const float DefaultCritMultiplier = 1.5f;
|
|
public const float DefaultBaseCrit = 0.05f;
|
|
|
|
/// <summary>
|
|
/// 计算最终伤害结果。
|
|
/// </summary>
|
|
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)
|
|
{
|
|
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)
|
|
: 0f;
|
|
float amplifiedDamage = rawDamage * (1f + damagePercent);
|
|
amplifiedDamage = Mathf.Max(amplifiedDamage, 1f);
|
|
|
|
// 4. 暴击判定 (CritChance & CritMultiplier)
|
|
float attackerCrit = context.AttackerPipeline != null
|
|
? context.AttackerPipeline.GetValue(StatType.CritChance)
|
|
: 0f;
|
|
|
|
float baseCrit = context.BaseCritChance;
|
|
if (baseCrit <= 0f && GameEntry.Luban != null && GameEntry.Luban.IsReady)
|
|
{
|
|
baseCrit = GameEntry.Luban.Global.WeaponBaseCrit;
|
|
}
|
|
|
|
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, 0.9f);
|
|
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
|
|
};
|
|
}
|
|
}
|
|
}
|