引入属性、伤害管线
This commit is contained in:
@@ -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);
|
||||
|
||||
+1
-7
@@ -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))
|
||||
|
||||
+1
-1
@@ -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(() =>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace SepCore.Entity
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
|
||||
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
|
||||
_weapon.Attack();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace SepCore.Entity
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
|
||||
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
|
||||
_weapon.Attack();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace SepCore.Entity
|
||||
|
||||
public override void OnEnter()
|
||||
{
|
||||
_weapon.ResetAttackCooldown(_weapon.WeaponData.Cooldown);
|
||||
_weapon.ResetAttackCooldown(_weapon.CurrentCooldown);
|
||||
_weapon.Attack();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user