引入属性、伤害管线
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user