178 lines
5.8 KiB
C#
178 lines
5.8 KiB
C#
using System.Collections.Generic;
|
|
using SepCore.Definition;
|
|
using DG.Tweening;
|
|
using SepCore.Entity;
|
|
using SepCore.CustomUtility;
|
|
using UnityEngine;
|
|
using UnityGameFramework.Runtime;
|
|
|
|
namespace SepCore.Entity
|
|
{
|
|
public class WeaponSlash : WeaponBase
|
|
{
|
|
#region Property
|
|
|
|
private Sequence _attackSequence;
|
|
private Vector3 _attackCenter;
|
|
private Quaternion _cachedAttackRotation;
|
|
private IWeaponAttackEffect _attackEffect;
|
|
|
|
[SerializeField] private float _attackDuration = 0.4f;
|
|
[SerializeField] private float _returnDuration = 0.4f;
|
|
[SerializeField] private float _swingAngleScale = 1f;
|
|
|
|
[SerializeField] private int _maxHitColliders = 32;
|
|
|
|
private Collider[] _hitResults;
|
|
private readonly HashSet<int> _hitEntityIds = new();
|
|
|
|
public float SectorAngle => WeaponData.GetParamFloat(ParamType.SectorAngle);
|
|
|
|
#endregion
|
|
|
|
protected override void BuildStates()
|
|
{
|
|
RegisterState(new WeaponSlashIdleState());
|
|
RegisterState(new WeaponSlashAimState());
|
|
RegisterState(new WeaponSlashSeekState());
|
|
RegisterState(new WeaponSlashAttackState());
|
|
}
|
|
|
|
public override void Attack()
|
|
{
|
|
StopAttackTween(false);
|
|
FaceTargetImmediately();
|
|
|
|
_isAttacking = true;
|
|
_attackCenter = CachedTransform.position;
|
|
_cachedAttackRotation = CachedTransform.rotation;
|
|
|
|
float swingAngle = Mathf.Clamp(SectorAngle * Mathf.Max(0.1f, _swingAngleScale), 15f, 170f);
|
|
Quaternion startRotation = Quaternion.AngleAxis(-swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation;
|
|
Quaternion endRotation = Quaternion.AngleAxis(swingAngle * 0.5f, Vector3.up) * _cachedAttackRotation;
|
|
CachedTransform.rotation = startRotation;
|
|
|
|
_attackSequence = DOTween.Sequence();
|
|
_attackSequence.Append(
|
|
CachedTransform.DORotateQuaternion(endRotation, _attackDuration).SetEase(Ease.OutSine));
|
|
_attackSequence.InsertCallback(_attackDuration * 0.5f, () =>
|
|
{
|
|
_attackEffect.Play(this, _attackCenter, _target, WeaponData.AttackRange);
|
|
ApplySectorDamage();
|
|
});
|
|
_attackSequence.Append(CachedTransform.DORotateQuaternion(_cachedAttackRotation, _returnDuration)
|
|
.SetEase(Ease.InSine));
|
|
_attackSequence.AppendCallback(() =>
|
|
{
|
|
_isAttacking = false;
|
|
_attackSequence = null;
|
|
});
|
|
}
|
|
|
|
private void ApplySectorDamage()
|
|
{
|
|
float attackRadius = WeaponData.AttackRange;
|
|
if (attackRadius <= 0f) return;
|
|
|
|
Vector3 forward = CachedTransform.forward;
|
|
forward.y = 0f;
|
|
if (forward.sqrMagnitude <= Mathf.Epsilon)
|
|
{
|
|
forward = Vector3.forward;
|
|
}
|
|
|
|
forward.Normalize();
|
|
float halfAngle = SectorAngle * 0.5f;
|
|
if (TryQueueSectorCollisionQuery(_attackCenter, attackRadius, in forward, halfAngle,
|
|
Mathf.Max(1, _maxHitColliders)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_hitResults == null || _hitResults.Length == 0) return;
|
|
|
|
int hitCount = Physics.OverlapSphereNonAlloc(_attackCenter, attackRadius, _hitResults, _hitMask,
|
|
QueryTriggerInteraction.Collide);
|
|
|
|
float attackRadiusSqr = attackRadius * attackRadius;
|
|
_hitEntityIds.Clear();
|
|
for (int i = 0; i < hitCount; i++)
|
|
{
|
|
Collider collider = _hitResults[i];
|
|
if (collider == null) continue;
|
|
|
|
TargetableObject targetable = collider.GetComponentInParent<TargetableObject>();
|
|
if (targetable == null || !targetable.Available || targetable.IsDead) continue;
|
|
if (!_hitEntityIds.Add(targetable.Id)) continue;
|
|
|
|
Vector3 toTarget = targetable.CachedTransform.position - _attackCenter;
|
|
toTarget.y = 0f;
|
|
|
|
float sqrDistance = toTarget.sqrMagnitude;
|
|
if (sqrDistance <= Mathf.Epsilon || sqrDistance > attackRadiusSqr) continue;
|
|
|
|
float angle = Vector3.Angle(forward, toTarget.normalized);
|
|
if (angle > halfAngle) continue;
|
|
|
|
AIUtility.PerformCollision(targetable, this);
|
|
}
|
|
}
|
|
|
|
#region Lifecycle
|
|
|
|
protected override bool OnWeaponShow(object userData)
|
|
{
|
|
_cachedAttackRotation = _cachedRotation;
|
|
|
|
int capacity = Mathf.Max(1, _maxHitColliders);
|
|
if (_hitResults == null || _hitResults.Length != capacity)
|
|
{
|
|
_hitResults = new Collider[capacity];
|
|
}
|
|
|
|
_attackEffect = new SlashSectorAttackEffect();
|
|
return true;
|
|
}
|
|
|
|
protected override void OnWeaponHide(object userData)
|
|
{
|
|
StopAttackTween(true);
|
|
_attackEffect = null;
|
|
}
|
|
|
|
protected override void OnWeaponDetach(EntityLogic parentEntity, object userData)
|
|
{
|
|
StopAttackTween(true);
|
|
base.OnWeaponDetach(parentEntity, userData);
|
|
}
|
|
|
|
protected override void OnEnabledChanged(bool enabled)
|
|
{
|
|
if (!enabled)
|
|
{
|
|
StopAttackTween(true);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void StopAttackTween(bool resetTransform)
|
|
{
|
|
if (_attackSequence != null)
|
|
{
|
|
_attackSequence.Kill();
|
|
_attackSequence = null;
|
|
}
|
|
|
|
_isAttacking = false;
|
|
|
|
if (resetTransform)
|
|
{
|
|
CachedTransform.rotation = _cachedAttackRotation;
|
|
}
|
|
|
|
_hitEntityIds.Clear();
|
|
}
|
|
}
|
|
}
|