2024-06-03 18:26:03 +00:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Linq;
|
|
|
|
using BlueWater.Audios;
|
|
|
|
using BlueWater.Interfaces;
|
|
|
|
using BlueWater.Uis;
|
|
|
|
using BlueWater.Utility;
|
|
|
|
using UnityEngine;
|
|
|
|
using Random = UnityEngine.Random;
|
|
|
|
|
|
|
|
namespace BlueWater.Players.Combat.Skills
|
|
|
|
{
|
|
|
|
public class TheWaltzOfTheSword : BaseSkill
|
|
|
|
{
|
|
|
|
private enum Direction
|
|
|
|
{
|
|
|
|
None = -1,
|
|
|
|
Left,
|
|
|
|
Back,
|
|
|
|
Right
|
|
|
|
}
|
|
|
|
|
|
|
|
private TheWaltzOfTheSwordData _theWaltzOfTheSwordData;
|
|
|
|
private CombatPlayer _combatPlayer;
|
|
|
|
private AnimationController _animationController;
|
|
|
|
private IPhysicMovable _iPhysicMovable;
|
|
|
|
|
|
|
|
private ParticleSystem _readyParticleInstance;
|
|
|
|
private ParticleSystem _attackParticleInstance;
|
|
|
|
private Coroutine _readyToSkillCoroutine;
|
|
|
|
|
|
|
|
private int _hitCount;
|
|
|
|
private int _attackCount;
|
|
|
|
private int _currentHitCount;
|
|
|
|
private float _intervalTime;
|
|
|
|
private Direction _currentDirection;
|
|
|
|
private bool _previousLeft;
|
|
|
|
private bool _isMoved;
|
|
|
|
private Vector3 _originStartPosition;
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
{
|
|
|
|
if (_readyParticleInstance)
|
|
|
|
{
|
|
|
|
Destroy(_readyParticleInstance.gameObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_attackParticleInstance)
|
|
|
|
{
|
|
|
|
Destroy(_attackParticleInstance.gameObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void InitializeData()
|
|
|
|
{
|
|
|
|
base.InitializeData();
|
|
|
|
|
|
|
|
if (!_combatPlayer)
|
|
|
|
{
|
|
|
|
_combatPlayer = SkillUser.GetComponent<CombatPlayer>();
|
|
|
|
_animationController = _combatPlayer.AnimationController;
|
|
|
|
_iPhysicMovable = _combatPlayer.GetComponent<IPhysicMovable>();
|
|
|
|
}
|
|
|
|
_theWaltzOfTheSwordData = (TheWaltzOfTheSwordData)SkillData;
|
|
|
|
_readyParticleInstance = Instantiate(_theWaltzOfTheSwordData.ReadyParticle, SkillUser.transform);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool CanSkill()
|
|
|
|
{
|
|
|
|
if (!EnableSkill) return false;
|
|
|
|
|
|
|
|
_originStartPosition = SkillUser.transform.position;
|
|
|
|
HitColliders = new Collider[_theWaltzOfTheSwordData.MaxAttackCount];
|
2024-06-14 09:11:35 +00:00
|
|
|
_hitCount = Physics.OverlapSphereNonAlloc(_originStartPosition, SkillData.Radius, HitColliders,
|
|
|
|
_theWaltzOfTheSwordData.TargetLayer, QueryTriggerInteraction.Collide);
|
2024-06-03 18:26:03 +00:00
|
|
|
|
|
|
|
return _hitCount > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void ActivateSkill(params Action[] onCompleted)
|
|
|
|
{
|
|
|
|
Utils.StartUniqueCoroutine(this, ref _readyToSkillCoroutine, ReadyToSkillCoroutine(onCompleted));
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator ReadyToSkillCoroutine(params Action[] onCompleted)
|
|
|
|
{
|
|
|
|
EnableSkill = false;
|
|
|
|
_iPhysicMovable.Rigidbody.linearVelocity = Vector3.zero;
|
|
|
|
_iPhysicMovable.Rigidbody.isKinematic = true;
|
|
|
|
_readyParticleInstance.Play();
|
|
|
|
SortCollidersByDistance();
|
|
|
|
_animationController.SetAnimationParameter("isReadyMainSkill", true);
|
|
|
|
|
|
|
|
var animationStarted = false;
|
|
|
|
yield return StartCoroutine(_animationController.WaitForAnimationToRun("ReadyToMainSkill",
|
|
|
|
success => animationStarted = success));
|
|
|
|
|
|
|
|
if (!animationStarted)
|
|
|
|
{
|
|
|
|
EndReadyToSkill(true, onCompleted[0]);
|
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_animationController.SetCurrentAnimationSpeed(SkillData.CastingTime);
|
|
|
|
|
|
|
|
while (_animationController.IsComparingCurrentAnimation("ReadyToMainSkill") &&
|
|
|
|
_animationController.GetCurrentAnimationNormalizedTime() < 1f)
|
|
|
|
{
|
|
|
|
yield return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndReadyToSkill(false, onCompleted[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void EndReadyToSkill(bool isEscaped, Action enableMove)
|
|
|
|
{
|
|
|
|
Utils.EndUniqueCoroutine(this, ref _readyToSkillCoroutine);
|
|
|
|
|
|
|
|
_animationController.ResetAnimationSpeed();
|
|
|
|
_animationController.SetAnimationParameter("isReadyMainSkill", false);
|
|
|
|
|
|
|
|
if (isEscaped)
|
|
|
|
{
|
|
|
|
_iPhysicMovable.Rigidbody.isKinematic = false;
|
|
|
|
enableMove?.Invoke();
|
|
|
|
StartCoroutine(Utils.CoolDownCoroutine(0, () => EnableSkill = true));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Utils.StartUniqueCoroutine(this, ref SkillCoroutineInstance, SkillCoroutine(enableMove));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator SkillCoroutine(params Action[] onCompleted)
|
|
|
|
{
|
|
|
|
_animationController.SetAnimationParameter("isActivateMainSkill", true);
|
|
|
|
|
|
|
|
var animationStarted = false;
|
|
|
|
yield return StartCoroutine(_animationController.WaitForAnimationToRun("MainSkill",
|
|
|
|
success => animationStarted = success));
|
|
|
|
|
|
|
|
if (!animationStarted)
|
|
|
|
{
|
|
|
|
EndSkill(onCompleted[0]);
|
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_animationController.SetCurrentAnimationSpeed(SkillData.Duration);
|
|
|
|
AudioManager.Instance.PlaySfx("CombatPlayerMainSkill", SkillData.Duration);
|
|
|
|
_intervalTime = 1f / _theWaltzOfTheSwordData.MaxAttackCount;
|
|
|
|
|
|
|
|
if (_theWaltzOfTheSwordData.IsFollowingTargetCamera)
|
|
|
|
{
|
|
|
|
CombatCameraManager.Instance.SetFollow(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
_currentDirection = Direction.Back;
|
|
|
|
_previousLeft = false;
|
|
|
|
_isMoved = false;
|
|
|
|
_attackCount = 0;
|
|
|
|
_currentHitCount = 0;
|
|
|
|
|
|
|
|
while (_animationController.IsComparingCurrentAnimation("MainSkill") && _attackCount < _theWaltzOfTheSwordData.MaxAttackCount)
|
|
|
|
{
|
|
|
|
if (!_isMoved)
|
|
|
|
{
|
|
|
|
if (AllTargetsAreDead())
|
|
|
|
{
|
|
|
|
EndSkill(onCompleted[0]);
|
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
|
|
|
|
MovePoint(HitColliders[_currentHitCount], _currentDirection);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (_animationController.GetCurrentAnimationNormalizedTime() >= _intervalTime * (_attackCount + 1))
|
|
|
|
{
|
|
|
|
ExecuteAttackRoutine(onCompleted[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
yield return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndSkill(onCompleted[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void MovePoint(Collider hitCollider, Direction direction)
|
|
|
|
{
|
|
|
|
var center = hitCollider.bounds.center;
|
|
|
|
var addX = 0f;
|
|
|
|
var addZ = 0f;
|
|
|
|
switch(direction)
|
|
|
|
{
|
|
|
|
case Direction.None:
|
|
|
|
break;
|
|
|
|
case Direction.Left:
|
|
|
|
_iPhysicMovable.CurrentDirection = Vector3.right;
|
|
|
|
addX = -hitCollider.bounds.extents.x;
|
|
|
|
_currentDirection = Direction.Back;
|
|
|
|
break;
|
|
|
|
case Direction.Back:
|
|
|
|
addZ = hitCollider.bounds.extents.z;
|
|
|
|
_currentDirection = _previousLeft ? Direction.Right : Direction.Left;
|
|
|
|
_previousLeft = !_previousLeft;
|
|
|
|
break;
|
|
|
|
case Direction.Right:
|
|
|
|
_iPhysicMovable.CurrentDirection = Vector3.left;
|
|
|
|
addX = hitCollider.bounds.extents.x;
|
|
|
|
_currentDirection = Direction.Back;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
var newPosition = new Vector3(center.x + addX, SkillUser.transform.position.y, center.z + addZ);
|
|
|
|
SkillUser.transform.position = newPosition;
|
|
|
|
|
|
|
|
_isMoved = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void ExecuteAttackRoutine(Action enableMove)
|
|
|
|
{
|
|
|
|
DoAttack(HitColliders[_currentHitCount]);
|
|
|
|
_attackCount++;
|
|
|
|
AddCurrentNum();
|
|
|
|
|
|
|
|
for (var i = 0; i < _hitCount; i++)
|
|
|
|
{
|
|
|
|
if (!IsTargetAlive(HitColliders[_currentHitCount]))
|
|
|
|
{
|
|
|
|
AddCurrentNum();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isMoved = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EndSkill(enableMove);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void AddCurrentNum()
|
|
|
|
{
|
|
|
|
_currentHitCount = (_currentHitCount + 1) % _hitCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void EndSkill(Action enableMove)
|
|
|
|
{
|
|
|
|
Utils.EndUniqueCoroutine(this, ref SkillCoroutineInstance);
|
|
|
|
|
|
|
|
AudioManager.Instance.StopSfx("CombatPlayerMainSkill");
|
|
|
|
_animationController.ResetAnimationSpeed();
|
|
|
|
_animationController.SetAnimationParameter("isActivateMainSkill", false);
|
|
|
|
|
|
|
|
enableMove?.Invoke();
|
|
|
|
_iPhysicMovable.Rigidbody.isKinematic = false;
|
|
|
|
if (_theWaltzOfTheSwordData.ReturnStartPosition)
|
|
|
|
{
|
|
|
|
SkillUser.transform.position = _originStartPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils.StartUniqueCoroutine(this, ref CooldownCoroutineInstance,Utils.CoolDownCoroutine(SkillData.Cooldown, EndCooldown));
|
|
|
|
CombatUiManager.Instance.CombatSkillUi.CoolDown(SkillData.Cooldown);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SortCollidersByDistance()
|
|
|
|
{
|
|
|
|
HitColliders = HitColliders
|
|
|
|
.Where(c => c != null)
|
|
|
|
.OrderBy(c => (c.transform.position - transform.position).sqrMagnitude)
|
|
|
|
.ToArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DoAttack(Collider hitCollider)
|
|
|
|
{
|
|
|
|
if (hitCollider == null || hitCollider.gameObject == null) return;
|
|
|
|
|
|
|
|
var iDamageable = hitCollider.GetComponentInParent<IDamageable>();
|
|
|
|
iDamageable.TakeDamage(SkillData.Damage);
|
|
|
|
|
|
|
|
if (iDamageable.CurrentHealthPoint == 0f)
|
|
|
|
{
|
|
|
|
if (_theWaltzOfTheSwordData.IsHitStop)
|
|
|
|
{
|
|
|
|
VisualFeedbackManager.Instance.CameraShake(CombatCameraManager.Instance.BaseCombatCamera);
|
|
|
|
VisualFeedbackManager.Instance.TriggerHitStop(_theWaltzOfTheSwordData.HitStopDuration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_theWaltzOfTheSwordData.AttackParticle)
|
|
|
|
{
|
|
|
|
var bounds = hitCollider.bounds;
|
|
|
|
var randomPosition = new Vector3(
|
|
|
|
Random.Range(bounds.min.x, bounds.max.x),
|
|
|
|
Random.Range(bounds.min.y, bounds.max.y),
|
|
|
|
Random.Range(bounds.min.z, bounds.max.z)
|
|
|
|
);
|
|
|
|
_attackParticleInstance = Instantiate(_theWaltzOfTheSwordData.AttackParticle, randomPosition, Quaternion.identity);
|
|
|
|
_attackParticleInstance.Play();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool IsTargetAlive(Collider hitCollider)
|
|
|
|
{
|
|
|
|
if (hitCollider == null || hitCollider.gameObject == null) return false;
|
|
|
|
|
|
|
|
var damageable = hitCollider.GetComponentInParent<IDamageable>();
|
|
|
|
return damageable is { CurrentHealthPoint: > 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool AllTargetsAreDead()
|
|
|
|
{
|
|
|
|
return HitColliders.All(c => !IsTargetAlive(c));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|