165 lines
4.9 KiB
C#
165 lines
4.9 KiB
C#
using SepCore.Battle;
|
|
using SepCore.CustomUtility;
|
|
using SepCore.Definition;
|
|
using UnityEngine;
|
|
|
|
namespace SepCore.Entity
|
|
{
|
|
public class MeleeEnemy : EnemyBase
|
|
{
|
|
private float _sqrAttackRange;
|
|
private EnemyStateType enemyState = EnemyStateType.Idle;
|
|
private TargetableObject _targetableTarget;
|
|
|
|
#region Lifecycle
|
|
|
|
protected override void OnShow(object userData)
|
|
{
|
|
base.OnShow(userData);
|
|
|
|
_sqrAttackRange = AttackRange * AttackRange;
|
|
enemyState = EnemyStateType.Idle;
|
|
_targetableTarget = null;
|
|
}
|
|
|
|
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
{
|
|
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
|
|
|
UpdateAttackState(elapseSeconds);
|
|
}
|
|
|
|
protected override void OnHide(bool isShutdown, object userData)
|
|
{
|
|
_targetableTarget = null;
|
|
enemyState = EnemyStateType.Idle;
|
|
|
|
base.OnHide(isShutdown, userData);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public override void SetTarget(Transform target)
|
|
{
|
|
base.SetTarget(target);
|
|
_targetableTarget = target != null ? target.GetComponent<TargetableObject>() : null;
|
|
}
|
|
|
|
private void UpdateAttackState(float elapseSeconds)
|
|
{
|
|
switch (enemyState)
|
|
{
|
|
case EnemyStateType.Idle:
|
|
Movement.SetMove(false);
|
|
if (HasValidTarget())
|
|
{
|
|
TransitionTo(EnemyStateType.Seek);
|
|
}
|
|
|
|
break;
|
|
case EnemyStateType.Seek:
|
|
if (!HasValidTarget())
|
|
{
|
|
TransitionTo(EnemyStateType.Idle);
|
|
return;
|
|
}
|
|
|
|
if (IsTargetInRange())
|
|
{
|
|
TransitionTo(EnemyStateType.Chase);
|
|
return;
|
|
}
|
|
|
|
Movement.SetMove(true);
|
|
Movement.SetDirection(GetTargetDirection());
|
|
break;
|
|
case EnemyStateType.Chase:
|
|
if (!HasValidTarget())
|
|
{
|
|
TransitionTo(EnemyStateType.Idle);
|
|
return;
|
|
}
|
|
|
|
Movement.SetMove(false);
|
|
if (!IsTargetInRange())
|
|
{
|
|
TransitionTo(EnemyStateType.Seek);
|
|
return;
|
|
}
|
|
|
|
if (_canAttack)
|
|
{
|
|
ResetAttackCooldown(AttackCooldown);
|
|
TransitionTo(EnemyStateType.Attack);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void TransitionTo(EnemyStateType newState)
|
|
{
|
|
enemyState = newState;
|
|
|
|
if (enemyState == EnemyStateType.Chase)
|
|
{
|
|
Movement.SetMove(false);
|
|
}
|
|
|
|
if (enemyState == EnemyStateType.Attack)
|
|
{
|
|
Movement.SetMove(false);
|
|
ExecuteAttack();
|
|
TransitionTo(EnemyStateType.Chase);
|
|
}
|
|
}
|
|
|
|
private bool HasValidTarget()
|
|
{
|
|
if (_target == null) return false;
|
|
|
|
if (_targetableTarget == null || _targetableTarget.CachedTransform != _target)
|
|
{
|
|
_targetableTarget = _target.GetComponent<TargetableObject>();
|
|
}
|
|
|
|
return _targetableTarget != null && _targetableTarget.Available && !_targetableTarget.IsDead;
|
|
}
|
|
|
|
private bool IsTargetInRange()
|
|
{
|
|
if (_target == null) return false;
|
|
|
|
Vector3 delta = _target.position - CachedTransform.position;
|
|
delta.y = 0f;
|
|
return delta.sqrMagnitude <= _sqrAttackRange;
|
|
}
|
|
|
|
private void ExecuteAttack()
|
|
{
|
|
if (!HasValidTarget() || !IsTargetInRange()) return;
|
|
|
|
ImpactData targetImpactData = _targetableTarget.GetImpactData();
|
|
ImpactData selfImpactData = GetImpactData();
|
|
if (AIUtility.GetRelation(selfImpactData.Camp, targetImpactData.Camp) == RelationType.Friendly)
|
|
{
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|