Phase 1: 复合属性修饰器管线与伤害结算系统
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -8,13 +8,11 @@ namespace SepCore.Components
|
||||
private Player _player;
|
||||
private Transform _target;
|
||||
|
||||
private float AbsorbRangeBase => GameEntry.Luban.Global.DefaultAbsorbRange / 1000f;
|
||||
private float AbsorbSpeed => GameEntry.Luban.Global.AbsorbSpeed / 1000f;
|
||||
private float CollectDistance => GameEntry.Luban.Global.CollectDistance / 1000f;
|
||||
public float AbsorbRange => GameEntry.Luban.Global.DefaultAbsorbRange;
|
||||
public float AbsorbSpeed => GameEntry.Luban.Global.AbsorbSpeed;
|
||||
public float CollectDistance => GameEntry.Luban.Global.CollectDistance;
|
||||
|
||||
private float AbsorbRange => AbsorbRangeBase;
|
||||
|
||||
public void OnInit(Player player, StatComponent statComponent)
|
||||
public void OnInit(Player player)
|
||||
{
|
||||
_player = player;
|
||||
_target = player.CachedTransform;
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ namespace SepCore.EnemyManager
|
||||
|
||||
_isStopped = false;
|
||||
_spawnEnemyMaxCount = GameEntry.Luban.Global.SpawnEnemyMaxCount;
|
||||
float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin / 1000f;
|
||||
float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax / 1000f;
|
||||
float minDistance = GameEntry.Luban.Global.EnemySpawnDistanceMin;
|
||||
float maxDistance = GameEntry.Luban.Global.EnemySpawnDistanceMax;
|
||||
_player = player.CachedTransform;
|
||||
_spawnPositionStrategy = new RandomCircleSpawnStrategy(minDistance, maxDistance);
|
||||
|
||||
|
||||
+3
-3
@@ -23,19 +23,19 @@ namespace SepCore.Simulation
|
||||
private float ResolveAbsorbRange()
|
||||
{
|
||||
if (_playerAbsorbRange >= 0f) return _playerAbsorbRange;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.DefaultAbsorbRange / 1000f : 2f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.DefaultAbsorbRange : 2f;
|
||||
}
|
||||
|
||||
private float ResolveAbsorbSpeed()
|
||||
{
|
||||
if (_playerAbsorbSpeed >= 0f) return _playerAbsorbSpeed;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.AbsorbSpeed / 1000f : 8f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.AbsorbSpeed : 8f;
|
||||
}
|
||||
|
||||
private float ResolveCollectDistance()
|
||||
{
|
||||
if (_playerCollectDistance >= 0f) return _playerCollectDistance;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.CollectDistance / 1000f : 0.5f;
|
||||
return GameEntry.Luban != null ? GameEntry.Luban.Global.CollectDistance : 0.5f;
|
||||
}
|
||||
|
||||
[Header("敌人互斥参数")]
|
||||
|
||||
@@ -216,7 +216,7 @@ namespace SepCore.Entity
|
||||
|
||||
_inputComponent.OnInit();
|
||||
_inputComponent.SetListening(true);
|
||||
_absorbComponent.OnInit(this, _statComponent);
|
||||
_absorbComponent.OnInit(this);
|
||||
_weaponEnabled = true;
|
||||
|
||||
_currentLevel = 0;
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace SepCore.Entity
|
||||
get
|
||||
{
|
||||
if (WeaponData == null) return 0f;
|
||||
float attackSpeed = _ownerAttributeProvider?.GetValue(StatType.AttackSpeed) ?? 0f;
|
||||
float speedFactor = Mathf.Max(0.2f, 1f + attackSpeed);
|
||||
float attackSpeed = _ownerAttributeProvider?.GetValue(StatType.AttackSpeed) ?? 1f;
|
||||
float speedFactor = Mathf.Max(0.2f, attackSpeed);
|
||||
return Mathf.Max(0.02f, WeaponData.Cooldown / speedFactor);
|
||||
}
|
||||
}
|
||||
@@ -46,8 +46,7 @@ namespace SepCore.Entity
|
||||
{
|
||||
if (WeaponData == null) return 0f;
|
||||
float rangeBonus = _ownerAttributeProvider?.GetValue(StatType.Range) ?? 0f;
|
||||
float effectiveBonus = rangeBonus >= 50f ? rangeBonus / 1000f : rangeBonus;
|
||||
return Mathf.Max(0.5f, WeaponData.AttackRange + effectiveBonus);
|
||||
return Mathf.Max(0.5f, WeaponData.AttackRange + rangeBonus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace SepCore.UI
|
||||
_selections.Clear();
|
||||
|
||||
int currentLevel = _currentLevel;
|
||||
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent / 1000f;
|
||||
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent;
|
||||
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
@@ -324,7 +324,7 @@ namespace SepCore.UI
|
||||
|
||||
public int CalculateRecyclePrice(int basePrice)
|
||||
{
|
||||
return Mathf.FloorToInt(basePrice * (GameEntry.Luban.Global.WeaponRecycleRate / 1000f));
|
||||
return Mathf.FloorToInt(basePrice * GameEntry.Luban.Global.WeaponRecycleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user