引入属性、伤害管线

This commit is contained in:
2026-09-18 21:43:22 +08:00
parent 92e1ea0d55
commit ba25ec1926
108 changed files with 2920 additions and 720 deletions
@@ -1,3 +1,4 @@
using SepCore.Battle;
using SepCore.CustomUtility;
using SepCore.Definition;
using UnityEngine;
@@ -145,9 +146,19 @@ namespace SepCore.Entity
return;
}
int damage = AIUtility.CalcDamageHP(AttackDamage, null, targetImpactData.DefenseStat,
targetImpactData.DodgeStat);
_targetableTarget.ApplyDamage(this, damage);
DamageContext context = new DamageContext
{
BaseDamage = AttackDamage,
DamageScalingStat = StatType.None,
ScalingRatio = 1.0f,
BaseCritChance = 0f,
CritMultiplier = 0f,
AttackerPipeline = selfImpactData.AttributeProvider,
DefenderPipeline = targetImpactData.AttributeProvider
};
DamageResult result = DamagePipeline.Calculate(in context);
_targetableTarget.ApplyDamage(this, in result, _targetableTarget.CachedTransform.position);
}
}
}
@@ -169,7 +169,7 @@ namespace SepCore.Entity
AddWeaponAsync(EntityDataFactory.Create(GameEntry.Entity.NextId(), WeaponType.Knife, Id, _playerData.Camp)).Forget();
// StatComponent
foreach (var modifier in role.Modifiers)
foreach (var modifier in role.StatModifierConfigs)
{
_statComponent.AddModifier(modifier);
}
@@ -189,7 +189,7 @@ namespace SepCore.Entity
public override ImpactData GetImpactData()
{
return new ImpactData(_playerData.Camp, 0, null, _healthComponent.DefenseStat, _healthComponent.DodgeStat);
return new ImpactData(_playerData.Camp, 0, _statComponent.Pipeline);
}
protected override void OnInit(object userData)
@@ -1,4 +1,5 @@
using SepCore.Components;
using SepCore.Battle;
using SepCore.Components;
using SepCore.Definition;
using SepCore.CustomUtility;
using UnityEngine;
@@ -18,22 +19,54 @@ namespace SepCore.Entity
public abstract ImpactData GetImpactData();
public void ApplyDamage(EntityBase attacker, int damageHP)
public void ApplyDamage(EntityBase attacker, in DamageResult damageResult, Vector3? hitPosition = null)
{
if (damageResult.IsDodge)
{
_healthComponent.TakeDamage(0, false, true, hitPosition);
return;
}
float fromHPRatio = _healthComponent.HealthRatio;
_healthComponent.TakeDamage(damageHP);
_healthComponent.TakeDamage(damageResult.Damage, damageResult.IsCrit, false, hitPosition);
float toHPRatio = _healthComponent.HealthRatio;
if (fromHPRatio > toHPRatio)
{
GameEntry.HPBar.ShowHPBar(this, fromHPRatio, toHPRatio);
}
if (damageResult.IsLifeSteal && attacker != null)
{
HealthComponent attackerHealth = attacker.GetComponent<HealthComponent>();
if (attackerHealth == null && attacker is WeaponBase weapon)
{
attackerHealth = weapon.GetComponentInParent<HealthComponent>();
}
if (attackerHealth != null)
{
attackerHealth.TryTriggerLifeSteal(damageResult.LifeStealAmount);
}
}
if (_healthComponent.CurrentHealth <= 0)
{
OnDead(attacker);
}
}
public void ApplyDamage(EntityBase attacker, int damageHP)
{
ApplyDamage(attacker, new DamageResult
{
Damage = damageHP,
IsDodge = false,
IsCrit = false,
IsLifeSteal = false,
LifeStealAmount = 0
});
}
protected override void OnInit(object userData)
{
base.OnInit(userData);
@@ -43,13 +43,7 @@ namespace SepCore.Entity
return false;
}
var simulationWorld = GameEntry.SimulationWorld;
if (simulationWorld == null)
{
return false;
}
if (!simulationWorld.TryGetNearestEnemyEntityId(weapon.CachedTransform.position, maxSqrRange,
if (!GameEntry.SimulationWorld.TryGetNearestEnemyEntityId(weapon.CachedTransform.position, maxSqrRange,
out int entityId))
{
return false;
@@ -27,16 +27,33 @@ namespace SepCore.Entity
protected EntityBase _target;
private float _sqrRange;
public EntityBase Target => _target;
public float SqrRange => _sqrRange;
public float CurrentCooldown
{
get
{
if (WeaponData == null) return 0f;
float attackSpeed = _ownerAttributeProvider?.GetValue(StatType.AttackSpeed) ?? 0f;
float speedFactor = Mathf.Max(0.2f, 1f + attackSpeed);
return Mathf.Max(0.02f, WeaponData.Cooldown / speedFactor);
}
}
protected StatProperty AttackStat { get; private set; } = new();
public float CurrentAttackRange
{
get
{
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);
}
}
private StatComponent _attackStatComponent;
private System.Action<modifier, bool> _attackStatCallback;
public float SqrRange => CurrentAttackRange * CurrentAttackRange;
private IAttributeProvider _ownerAttributeProvider;
private static float _rotateSpeed => GameEntry.Luban.Global.WeaponRotateSpeed;
@@ -63,8 +80,7 @@ namespace SepCore.Entity
return;
}
StartAttackCooldown(WeaponData.Cooldown);
_sqrRange = WeaponData.AttackRange * WeaponData.AttackRange;
StartAttackCooldown(CurrentCooldown);
_cachedRotation = CachedTransform.rotation;
if (WeaponData.OwnerCamp == CampType.Player)
@@ -136,14 +152,33 @@ namespace SepCore.Entity
public virtual ImpactData GetImpactData()
{
return new ImpactData(WeaponData.OwnerCamp, WeaponData.Attack, AttackStat);
float baseCrit = GameEntry.Luban != null && GameEntry.Luban.IsReady
? GameEntry.Luban.Global.WeaponBaseCrit
: 0.05f;
float critMult = GameEntry.Luban != null && GameEntry.Luban.IsReady
? GameEntry.Luban.Global.CritMultiplier
: 1.5f;
return new ImpactData(
WeaponData.OwnerCamp,
WeaponData.Attack,
_ownerAttributeProvider,
WeaponData.DamageType,
1.0f,
baseCrit,
critMult);
}
public abstract void Attack();
public void ResetAttackCooldown()
{
ResetAttackCooldown(CurrentCooldown);
}
public virtual void Check()
{
_target = SelectTarget(_sqrRange);
_target = SelectTarget(SqrRange);
}
public virtual void RotateToTarget(float elapseSeconds)
@@ -272,30 +307,18 @@ namespace SepCore.Entity
protected void BindAttackStatFromOwner(EntityLogic parentEntity)
{
ReleaseAttackStatSubscription();
AttackStat = new StatProperty();
if (parentEntity is not Player player) return;
var statComponent = player.GetComponent<StatComponent>();
if (statComponent == null) return;
AttackStat = statComponent.GetStat(StatType.Attack);
_attackStatComponent = statComponent;
_attackStatCallback = (modifier, isApply) =>
statComponent.UpdateStat(AttackStat, modifier, isApply);
statComponent.Subscribe(StatType.Attack, _attackStatCallback);
_ownerAttributeProvider = statComponent.Pipeline;
}
protected void ReleaseAttackStatSubscription()
{
if (_attackStatComponent != null && _attackStatCallback != null)
{
_attackStatComponent.Unsubscribe(StatType.Attack, _attackStatCallback);
}
_attackStatComponent = null;
_attackStatCallback = null;
AttackStat = new StatProperty();
_ownerAttributeProvider = null;
}
}
}
@@ -55,7 +55,7 @@ namespace SepCore.Entity
Vector3 fireOrigin = CachedTransform.TransformPoint(_fireOriginOffset);
Vector3 directionToTarget = targetPosition - fireOrigin;
float maxDistance = WeaponData.AttackRange;
float maxDistance = CurrentAttackRange;
if (directionToTarget.sqrMagnitude > Mathf.Epsilon &&
Physics.Raycast(fireOrigin, directionToTarget.normalized, out RaycastHit hit, maxDistance, _hitMask,
QueryTriggerInteraction.Collide))
@@ -15,7 +15,7 @@ namespace SepCore.Entity
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
_weapon.Attack();
}
@@ -42,7 +42,7 @@ namespace SepCore.Entity
_attackCenter = _target != null ? _target.CachedTransform.position : CachedTransform.position;
CachedTransform.SetParent(null);
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * WeaponData.AttackRange;
Vector3 targetPos = CachedTransform.position + CachedTransform.forward * CurrentAttackRange;
_attackSequence = DOTween.Sequence();
_attackSequence.Append(CachedTransform.DOMove(targetPos, _attackDuration).SetEase(Ease.OutQuad));
_attackSequence.AppendCallback(() =>
@@ -15,7 +15,7 @@ namespace SepCore.Entity
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
_weapon.Attack();
}
@@ -15,7 +15,7 @@ namespace SepCore.Entity
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
_weapon.Attack();
}
@@ -15,7 +15,7 @@ namespace SepCore.Entity
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
_weapon.Attack();
}
@@ -57,7 +57,7 @@ namespace SepCore.Entity
CachedTransform.DORotateQuaternion(endRotation, _attackDuration).SetEase(Ease.OutSine));
_attackSequence.InsertCallback(_attackDuration * 0.5f, () =>
{
_attackEffect.Play(this, _attackCenter, _target, WeaponData.AttackRange);
_attackEffect.Play(this, _attackCenter, _target, CurrentAttackRange);
ApplySectorDamage();
});
_attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, _returnDuration)
@@ -71,7 +71,7 @@ namespace SepCore.Entity
private void ApplySectorDamage()
{
float attackRadius = WeaponData.AttackRange;
float attackRadius = CurrentAttackRange;
if (attackRadius <= 0f) return;
Vector3 forward = CachedTransform.forward;
@@ -15,7 +15,7 @@ namespace SepCore.Entity
public override void OnEnter()
{
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
_weapon.Attack();
}