CapersProject/Assets/02.Scripts/Skill/Player/Combat/TheWaltzOfTheSword.cs

314 lines
11 KiB
C#

using System;
using System.Collections;
using System.Linq;
using BlueWater.Audios;
using BlueWater.Interfaces;
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;
}
public override bool CanSkill()
{
if (!EnableSkill) return false;
_originStartPosition = SkillUser.transform.position;
HitColliders = new Collider[_theWaltzOfTheSwordData.MaxAttackCount];
_hitCount = Physics.OverlapSphereNonAlloc(_originStartPosition, SkillData.Radius, HitColliders,
_theWaltzOfTheSwordData.TargetLayer, QueryTriggerInteraction.Collide);
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 = Instantiate(_theWaltzOfTheSwordData.ReadyParticle, SkillUser.transform);
_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.SetCurrentDirection(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.SetCurrentDirection(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));
}
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));
}
}
}