Phase 1: 复合属性修饰器管线与伤害结算系统

This commit is contained in:
2026-09-24 22:37:22 +08:00
parent ba25ec1926
commit 02f4c67464
35 changed files with 334 additions and 141 deletions
@@ -13,6 +13,7 @@ namespace SepCore.Battle
{
private readonly List<StatModifier> _modifiers = new();
private float _baseValue;
private bool _isBaseValueExplicitlySet;
private bool _isDirty = true;
private float _cachedValue;
private StatConfig _config;
@@ -26,6 +27,11 @@ namespace SepCore.Battle
if (_config == null && GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
_config = GameEntry.Luban.Get<StatConfig>((int)Type);
if (!_isBaseValueExplicitlySet && _config != null)
{
_baseValue = _config.DefaultBaseValue;
_isDirty = true;
}
}
return _config;
@@ -33,15 +39,35 @@ namespace SepCore.Battle
set
{
_config = value;
if (!_isBaseValueExplicitlySet && _config != null)
{
_baseValue = _config.DefaultBaseValue;
}
_isDirty = true;
}
}
public float BaseValue
{
get => _baseValue;
get
{
if (!_isBaseValueExplicitlySet)
{
if (Config != null)
{
_baseValue = Config.DefaultBaseValue;
}
else if (Type is StatType.DamagePercent or StatType.AttackSpeed)
{
_baseValue = 1.0f;
}
}
return _baseValue;
}
set
{
_isBaseValueExplicitlySet = true;
if (Math.Abs(_baseValue - value) > 1e-6f)
{
_baseValue = value;
@@ -69,8 +95,21 @@ namespace SepCore.Battle
public CharacterStat(StatType type, float baseValue = 0f, StatConfig config = null)
{
Type = type;
_baseValue = baseValue;
_config = config;
if (Math.Abs(baseValue) > 1e-6f)
{
_baseValue = baseValue;
_isBaseValueExplicitlySet = true;
}
else if (_config != null)
{
_baseValue = _config.DefaultBaseValue;
_isBaseValueExplicitlySet = true;
}
else if (type is StatType.DamagePercent or StatType.AttackSpeed)
{
_baseValue = 1.0f;
}
_isDirty = true;
}
@@ -154,7 +193,7 @@ namespace SepCore.Battle
}
}
float finalValue = ((_baseValue + sumFlat) * (1f + sumPercentAdd)) * prodPercentMult;
float finalValue = ((BaseValue + sumFlat) * (1f + sumPercentAdd)) * prodPercentMult;
return ClampStat(finalValue);
}
@@ -26,7 +26,15 @@ namespace SepCore.Battle
float maxDodge = 0.8f;
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
StatConfig dodgeConfig = GameEntry.Luban.Get<StatConfig>((int)StatType.Dodge);
if (dodgeConfig != null && dodgeConfig.HasMax)
{
maxDodge = dodgeConfig.MaxValue;
}
else
{
maxDodge = GameEntry.Luban.Global.MaxDodgeRate / 1000f;
}
}
dodgeChance = Mathf.Clamp(dodgeChance, 0f, maxDodge);
@@ -55,9 +63,11 @@ namespace SepCore.Battle
// 3. 全局百分比增伤放大 (DamagePercent)
float damagePercent = context.AttackerPipeline != null
? context.AttackerPipeline.GetValue(StatType.DamagePercent)
: 0f;
float amplifiedDamage = rawDamage * (1f + damagePercent);
amplifiedDamage = Mathf.Max(amplifiedDamage, 1f);
: 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
@@ -65,12 +75,16 @@ namespace SepCore.Battle
: 0f;
float baseCrit = context.BaseCritChance;
if (baseCrit <= 0f && GameEntry.Luban != null && GameEntry.Luban.IsReady)
float maxCrit = 0.9f;
if (GameEntry.Luban != null && GameEntry.Luban.IsReady)
{
baseCrit = GameEntry.Luban.Global.WeaponBaseCrit;
StatConfig critConfig = GameEntry.Luban.Get<StatConfig>((int)StatType.CritChance);
if (critConfig != null && critConfig.HasMax)
{
maxCrit = critConfig.MaxValue;
}
}
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, 0.9f);
float totalCritChance = Mathf.Clamp(baseCrit + attackerCrit, 0f, maxCrit);
bool isCrit = totalCritChance > 0f && Random.value < totalCritChance;
float critMultiplier = context.CritMultiplier;